Skip to content

Commit 30f317f

Browse files
committed
Feat: Add LINQ-like extension methods for Span and ReadOnlySpan
1 parent 574d867 commit 30f317f

13 files changed

+423
-2
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// ---------------------------------------------------------------------------------------------------------------------
2+
// Imports
3+
// ---------------------------------------------------------------------------------------------------------------------
4+
// ReSharper disable once CheckNamespace
5+
namespace CodeOfChaos.SpanLINQ;
6+
// ---------------------------------------------------------------------------------------------------------------------
7+
// Code
8+
// ---------------------------------------------------------------------------------------------------------------------
9+
public static class SpanLinqAllExtensions {
10+
public static bool All<T>(this in ReadOnlySpan<T> span, Func<T, bool> predicate) {
11+
for (int i = span.Length - 1; i >= 0; i--)
12+
if (!predicate(span[i])) return false;
13+
return true;
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// ---------------------------------------------------------------------------------------------------------------------
2+
// Imports
3+
// ---------------------------------------------------------------------------------------------------------------------
4+
// ReSharper disable once CheckNamespace
5+
namespace CodeOfChaos.SpanLINQ;
6+
// ---------------------------------------------------------------------------------------------------------------------
7+
// Code
8+
// ---------------------------------------------------------------------------------------------------------------------
9+
public static class SpanLinqAnyExtensions {
10+
public static bool Any<T>(this in ReadOnlySpan<T> span, Func<T, bool> predicate) {
11+
for (int i = span.Length - 1; i >= 0; i--)
12+
if (predicate(span[i])) return true;
13+
return false;
14+
}
15+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// ---------------------------------------------------------------------------------------------------------------------
2+
// Imports
3+
// ---------------------------------------------------------------------------------------------------------------------
4+
// ReSharper disable once CheckNamespace
5+
namespace System;
6+
7+
// ---------------------------------------------------------------------------------------------------------------------
8+
// Code
9+
// ---------------------------------------------------------------------------------------------------------------------
10+
public static class SpanLinqAverageExtensions {
11+
public static double Average<T>(this in Span<T> span, Func<T, double> selector) {
12+
int length = span.Length;
13+
if (length == 0) throw new InvalidOperationException("Sequence contains no elements");
14+
double sum = 0;
15+
for (int i = span.Length - 1; i >= 0; i--) {
16+
sum += selector(span[i]);
17+
}
18+
return sum / length;
19+
}
20+
21+
public static double Average<T>(this in ReadOnlySpan<T> span, Func<T, double> selector) {
22+
int length = span.Length;
23+
if (length == 0) throw new InvalidOperationException("Sequence contains no elements");
24+
double sum = 0;
25+
for (int i = span.Length - 1; i >= 0; i--) {
26+
sum += selector(span[i]);
27+
}
28+
return sum / length;
29+
}
30+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// ---------------------------------------------------------------------------------------------------------------------
2+
// Imports
3+
// ---------------------------------------------------------------------------------------------------------------------
4+
// ReSharper disable once CheckNamespace
5+
namespace CodeOfChaos.SpanLINQ;
6+
7+
// ---------------------------------------------------------------------------------------------------------------------
8+
// Code
9+
// ---------------------------------------------------------------------------------------------------------------------
10+
public static class SpanLinqCountExtensions {
11+
public static int Count<T>(this in Span<T> span, Func<T, bool> predicate) {
12+
int count = 0;
13+
for (int index = span.Length - 1; index >= 0; index--) {
14+
if (predicate(span[index])) count++;
15+
}
16+
return count;
17+
}
18+
19+
public static int Count<T>(this in ReadOnlySpan<T> span, Func<T, bool> predicate) {
20+
int count = 0;
21+
for (int index = span.Length - 1; index >= 0; index--) {
22+
if (predicate(span[index])) count++;
23+
}
24+
return count;
25+
}
26+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// ---------------------------------------------------------------------------------------------------------------------
2+
// Imports
3+
// ---------------------------------------------------------------------------------------------------------------------
4+
// ReSharper disable once CheckNamespace
5+
namespace CodeOfChaos.SpanLINQ;
6+
// ---------------------------------------------------------------------------------------------------------------------
7+
// Code
8+
// ---------------------------------------------------------------------------------------------------------------------
9+
public static class SpanLinqFirstExtensions {
10+
public static T First<T>(this in Span<T> span, Func<T, bool> predicate) {
11+
// ReSharper disable once ForCanBeConvertedToForeach
12+
for (int i = 0; i < span.Length; i++) {
13+
T element = span[i];
14+
if (predicate(element)) return element;
15+
}
16+
17+
throw new InvalidOperationException("Sequence contains no elements");
18+
}
19+
20+
public static T First<T>(this in ReadOnlySpan<T> span, Func<T, bool> predicate) {
21+
// ReSharper disable once ForCanBeConvertedToForeach
22+
for (int i = 0; i < span.Length; i++) {
23+
T element = span[i];
24+
if (predicate(element)) return element;
25+
}
26+
27+
throw new InvalidOperationException("Sequence contains no elements");
28+
}
29+
30+
public static T? FirstOrDefault<T>(this in Span<T> span, Func<T, bool> predicate) {
31+
// ReSharper disable once ForCanBeConvertedToForeach
32+
for (int i = 0; i < span.Length; i++) {
33+
T element = span[i];
34+
if (predicate(element)) return element;
35+
}
36+
37+
return default;
38+
}
39+
40+
public static T? FirstOrDefault<T>(this in ReadOnlySpan<T> span, Func<T, bool> predicate) {
41+
// ReSharper disable once ForCanBeConvertedToForeach
42+
for (int i = 0; i < span.Length; i++) {
43+
T element = span[i];
44+
if (predicate(element)) return element;
45+
}
46+
47+
return default;
48+
}
49+
50+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// ---------------------------------------------------------------------------------------------------------------------
2+
// Imports
3+
// ---------------------------------------------------------------------------------------------------------------------
4+
// ReSharper disable once CheckNamespace
5+
namespace CodeOfChaos.SpanLINQ;
6+
// ---------------------------------------------------------------------------------------------------------------------
7+
// Code
8+
// ---------------------------------------------------------------------------------------------------------------------
9+
public static class SpanLinqLastExtensions {
10+
public static T Last<T>(this in Span<T> span, Func<T, bool> predicate) {
11+
// ReSharper disable once ForCanBeConvertedToForeach
12+
for (int i = span.Length - 1; i >= 0; i--) {
13+
T element = span[i];
14+
if (predicate(element)) return element;
15+
}
16+
17+
throw new InvalidOperationException("Sequence contains no elements");
18+
}
19+
20+
public static T Last<T>(this in ReadOnlySpan<T> span, Func<T, bool> predicate) {
21+
// ReSharper disable once ForCanBeConvertedToForeach
22+
for (int i = span.Length - 1; i >= 0; i--) {
23+
T element = span[i];
24+
if (predicate(element)) return element;
25+
}
26+
27+
throw new InvalidOperationException("Sequence contains no elements");
28+
}
29+
30+
public static T? LastOrDefault<T>(this in Span<T> span, Func<T, bool> predicate) {
31+
// ReSharper disable once ForCanBeConvertedToForeach
32+
for (int i = span.Length - 1; i >= 0; i--) {
33+
T element = span[i];
34+
if (predicate(element)) return element;
35+
}
36+
37+
return default;
38+
}
39+
40+
public static T? LastOrDefault<T>(this in ReadOnlySpan<T> span, Func<T, bool> predicate) {
41+
// ReSharper disable once ForCanBeConvertedToForeach
42+
for (int i = span.Length - 1; i >= 0; i--) {
43+
T element = span[i];
44+
if (predicate(element)) return element;
45+
}
46+
47+
return default;
48+
}
49+
50+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// ---------------------------------------------------------------------------------------------------------------------
2+
// Imports
3+
// ---------------------------------------------------------------------------------------------------------------------
4+
// ReSharper disable once CheckNamespace
5+
namespace CodeOfChaos.SpanLINQ;
6+
// ---------------------------------------------------------------------------------------------------------------------
7+
// Code
8+
// ---------------------------------------------------------------------------------------------------------------------
9+
public static class SpanLinqMaxExtensions {
10+
public static TResult Max<T, TResult>(this in Span<T> span, Func<T, TResult> selector)
11+
where TResult : IComparable<TResult> {
12+
if (span.Length == 0) throw new InvalidOperationException("Sequence contains no elements");
13+
14+
TResult min = selector(span[0]);
15+
for (int i = span.Length - 1; i >= 1; i--) {
16+
TResult current = selector(span[i]);
17+
if (current.CompareTo(min) > 0) min = current;
18+
}
19+
20+
return min;
21+
}
22+
23+
public static TResult Max<T, TResult>(this in ReadOnlySpan<T> span, Func<T, TResult> selector)
24+
where TResult : IComparable<TResult> {
25+
if (span.Length == 0) throw new InvalidOperationException("Sequence contains no elements");
26+
27+
TResult min = selector(span[0]);
28+
for (int i = span.Length - 1; i >= 1; i--) {
29+
TResult current = selector(span[i]);
30+
if (current.CompareTo(min) > 0) min = current;
31+
}
32+
33+
return min;
34+
}
35+
36+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// ---------------------------------------------------------------------------------------------------------------------
2+
// Imports
3+
// ---------------------------------------------------------------------------------------------------------------------
4+
// ReSharper disable once CheckNamespace
5+
namespace CodeOfChaos.SpanLINQ;
6+
// ---------------------------------------------------------------------------------------------------------------------
7+
// Code
8+
// ---------------------------------------------------------------------------------------------------------------------
9+
public static class SpanLinqMinExtensions {
10+
public static TResult Min<T, TResult>(this in Span<T> span, Func<T, TResult> selector)
11+
where TResult : IComparable<TResult> {
12+
if (span.Length == 0) throw new InvalidOperationException("Sequence contains no elements");
13+
14+
TResult min = selector(span[0]);
15+
for (int i = span.Length - 1; i >= 1; i--) {
16+
TResult current = selector(span[i]);
17+
if (current.CompareTo(min) < 0) min = current;
18+
}
19+
20+
return min;
21+
}
22+
23+
public static TResult Min<T, TResult>(this in ReadOnlySpan<T> span, Func<T, TResult> selector)
24+
where TResult : IComparable<TResult> {
25+
if (span.Length == 0) throw new InvalidOperationException("Sequence contains no elements");
26+
27+
TResult min = selector(span[0]);
28+
for (int i = span.Length - 1; i >= 1; i--) {
29+
TResult current = selector(span[i]);
30+
if (current.CompareTo(min) < 0) min = current;
31+
}
32+
33+
return min;
34+
}
35+
36+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// ---------------------------------------------------------------------------------------------------------------------
2+
// Imports
3+
// ---------------------------------------------------------------------------------------------------------------------
4+
// ReSharper disable once CheckNamespace
5+
namespace CodeOfChaos.SpanLINQ;
6+
// ---------------------------------------------------------------------------------------------------------------------
7+
// Code
8+
// ---------------------------------------------------------------------------------------------------------------------
9+
public static class SpanLinqSelectExtensions {
10+
11+
public static void Select<TSource, TResult>(
12+
this in ReadOnlySpan<TSource> source,
13+
Func<TSource, TResult> selector,
14+
Span<TResult> destination
15+
) {
16+
if (source.Length != destination.Length) throw new ArgumentException("Destination span is too small");
17+
18+
for (int i = 0; i < source.Length; i++) {
19+
destination[i] = selector(source[i]);
20+
}
21+
}
22+
23+
public static void SelectMany<TSource, TResult>(
24+
this in ReadOnlySpan<TSource> source,
25+
Func<TSource, ReadOnlySpan<TResult>> selector,
26+
Span<TResult> destination,
27+
out int totalCount
28+
) {
29+
if (source.Length > destination.Length) throw new ArgumentException("Destination span is too small");
30+
31+
int destinationMaxCount = destination.Length;
32+
totalCount = 0;
33+
34+
// ReSharper disable once ForCanBeConvertedToForeach
35+
for (int i = 0; i < source.Length; i++) {
36+
ReadOnlySpan<TResult> nested = selector(source[i]);
37+
if (totalCount + nested.Length > destinationMaxCount) throw new ArgumentException("Destination span is too small");
38+
nested.CopyTo(destination[totalCount..]);
39+
totalCount += nested.Length;
40+
}
41+
}
42+
43+
}

src/CodeOfChaos.Extensions/SpanLinq/SpanLinqSumExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Imports
33
// ---------------------------------------------------------------------------------------------------------------------
44
// ReSharper disable once CheckNamespace
5-
namespace System;
5+
namespace CodeOfChaos.SpanLINQ;
66

77
// ---------------------------------------------------------------------------------------------------------------------
88
// Code

0 commit comments

Comments
 (0)