-
-
Save Pzixel/246df4519121d99fb080f04d82d82200 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
namespace Extensions.Linq | |
{ | |
public static class XEnumerable | |
{ | |
public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> source) where T : class => | |
source.Where(x => x is {})!; | |
public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> source) where T : struct => | |
source.Where(x => x is {}).Select(x => x.GetValueOrDefault()); | |
public static T? FirstOrNull<T>(this IEnumerable<T> source) where T : class => | |
source.FirstOrDefault(); | |
public static T? FirstOrNull<T>(this IEnumerable<T?> source) where T : struct => | |
source.FirstOrDefault(); | |
public static T? FirstOrNull<T>(this IEnumerable<T> source, object? _ = null) where T : struct => | |
source.Cast<T?>().FirstOrDefault(); | |
public static T? SingleOrNull<T>(this IEnumerable<T> source) where T : class => | |
source.SingleOrDefault(); | |
public static T? SingleOrNull<T>(this IEnumerable<T> source, object? _ = null) where T : struct => | |
source.Cast<T?>().SingleOrDefault(); | |
public static IEnumerable<TResult> SelectWithState<T, TResult, TState>( | |
this IEnumerable<T> source, | |
Func<T, TState, (TResult Result, TState NewState)> mapFunc, | |
TState initialState) | |
{ | |
var state = initialState; | |
return source.Select(x => | |
{ | |
var (result, newState) = mapFunc(x, state); | |
state = newState; | |
return result; | |
}); | |
} | |
public static TSource? MaxByOrNull<TSource, TKey>( | |
this IEnumerable<TSource> source, | |
Func<TSource, TKey> selector, | |
IComparer<TKey>? comparer = null) where TSource : class | |
{ | |
if (source == null) throw new ArgumentNullException(nameof(source)); | |
if (selector == null) throw new ArgumentNullException(nameof(selector)); | |
comparer ??= Comparer<TKey>.Default; | |
using (var sourceIterator = source.GetEnumerator()) | |
{ | |
if (!sourceIterator.MoveNext()) | |
return default; | |
var max = sourceIterator.Current; | |
var maxKey = selector(max); | |
while (sourceIterator.MoveNext()) | |
{ | |
var candidate = sourceIterator.Current; | |
var candidateProjected = selector(candidate); | |
if (comparer.Compare(candidateProjected, maxKey) > 0) | |
{ | |
max = candidate; | |
maxKey = candidateProjected; | |
} | |
} | |
return max; | |
} | |
} | |
public static TSource? MaxByOrNull<TSource, TKey>( | |
this IEnumerable<TSource> source, | |
Func<TSource, TKey> selector, | |
IComparer<TKey>? comparer = null, | |
object? _ = null) where TSource : struct | |
{ | |
if (source == null) throw new ArgumentNullException(nameof(source)); | |
if (selector == null) throw new ArgumentNullException(nameof(selector)); | |
comparer ??= Comparer<TKey>.Default; | |
using (var sourceIterator = source.GetEnumerator()) | |
{ | |
if (!sourceIterator.MoveNext()) | |
return default; | |
var max = sourceIterator.Current; | |
var maxKey = selector(max); | |
while (sourceIterator.MoveNext()) | |
{ | |
var candidate = sourceIterator.Current; | |
var candidateProjected = selector(candidate); | |
if (comparer.Compare(candidateProjected, maxKey) > 0) | |
{ | |
max = candidate; | |
maxKey = candidateProjected; | |
} | |
} | |
return max; | |
} | |
} | |
public static TSource? MinByOrNull<TSource, TKey>( | |
this IEnumerable<TSource> source, | |
Func<TSource, TKey> selector, | |
IComparer<TKey>? comparer = null) where TSource : class | |
{ | |
if (source == null) throw new ArgumentNullException(nameof(source)); | |
if (selector == null) throw new ArgumentNullException(nameof(selector)); | |
comparer ??= Comparer<TKey>.Default; | |
using var sourceIterator = source.GetEnumerator(); | |
if (!sourceIterator.MoveNext()) | |
return default; | |
var min = sourceIterator.Current; | |
var minKey = selector(min); | |
while (sourceIterator.MoveNext()) | |
{ | |
var candidate = sourceIterator.Current; | |
var candidateProjected = selector(candidate); | |
if (comparer.Compare(candidateProjected, minKey) < 0) | |
{ | |
min = candidate; | |
minKey = candidateProjected; | |
} | |
} | |
return min; | |
} | |
public static TSource? MinByOrNull<TSource, TKey>( | |
this IEnumerable<TSource> source, | |
Func<TSource, TKey> selector, | |
IComparer<TKey>? comparer = null, | |
object? _ = null) where TSource : struct | |
{ | |
if (source == null) throw new ArgumentNullException(nameof(source)); | |
if (selector == null) throw new ArgumentNullException(nameof(selector)); | |
comparer ??= Comparer<TKey>.Default; | |
using var sourceIterator = source.GetEnumerator(); | |
if (!sourceIterator.MoveNext()) | |
return default; | |
var min = sourceIterator.Current; | |
var minKey = selector(min); | |
while (sourceIterator.MoveNext()) | |
{ | |
var candidate = sourceIterator.Current; | |
var candidateProjected = selector(candidate); | |
if (comparer.Compare(candidateProjected, minKey) < 0) | |
{ | |
min = candidate; | |
minKey = candidateProjected; | |
} | |
} | |
return min; | |
} | |
public static IEnumerable<TSource> Except<TSource>(this IEnumerable<TSource> source, TSource element, | |
IEqualityComparer<TSource>? comparer = null) | |
{ | |
if (source == null) throw new ArgumentNullException(nameof(source)); | |
if (element is null) throw new ArgumentNullException(nameof(element)); | |
comparer ??= EqualityComparer<TSource>.Default; | |
using (var sourceIterator = source.GetEnumerator()) | |
{ | |
while (sourceIterator.MoveNext()) | |
{ | |
var candidate = sourceIterator.Current; | |
if (comparer.Equals(candidate, element)) | |
continue; | |
yield return candidate; | |
} | |
} | |
} | |
public static IEnumerable<T[]> SliceOn<T>(this IEnumerable<T> source, int batchCapacity) | |
{ | |
if (source is null) | |
throw new ArgumentNullException(nameof(source)); | |
return source.SliceOnInternal(batchCapacity); | |
} | |
private static IEnumerable<T[]> SliceOnInternal<T>(this IEnumerable<T> source, int batchCapacity) | |
{ | |
var buffer = new List<T>(batchCapacity); | |
foreach (var item in source) | |
{ | |
buffer.Add(item); | |
if (buffer.Count == batchCapacity) | |
{ | |
yield return buffer.ToArray(); | |
buffer.Clear(); | |
} | |
} | |
if (buffer.Count > 0) | |
yield return buffer.ToArray(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment