#if !IL2CPP using System; using System.Collections.Generic; // Since EHR was originally made for net6.0 and uses the newer LINQ methods from it, // and migrating to netstandard2.1 removed these, // it's the cleanest and easiest to reimplement these as extension methods. namespace EHR { public static class LinqExtensions { // ============================================================ // MinBy // ============================================================ public static TSource MinBy( this IEnumerable source, Func keySelector) { return source.MinBy(keySelector, null); } public static TSource MinBy( this IEnumerable source, Func keySelector, IComparer comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); comparer ??= Comparer.Default; using IEnumerator enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) throw new InvalidOperationException("Sequence contains no elements."); TSource min = enumerator.Current; TKey minKey = keySelector(min); while (enumerator.MoveNext()) { TSource current = enumerator.Current; TKey currentKey = keySelector(current); if (comparer.Compare(currentKey, minKey) < 0) { min = current; minKey = currentKey; } } return min; } public static TSource MinBy( this IReadOnlyList source, Func keySelector) { return source.MinBy(keySelector, null); } public static TSource MinBy( this IReadOnlyList source, Func keySelector, IComparer comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); comparer ??= Comparer.Default; int count = source.Count; if (count == 0) throw new InvalidOperationException("Sequence contains no elements."); TSource min = source[0]; TKey minKey = keySelector(min); for (int i = 1; i < count; i++) { TSource current = source[i]; TKey currentKey = keySelector(current); if (comparer.Compare(currentKey, minKey) < 0) { min = current; minKey = currentKey; } } return min; } // ============================================================ // MaxBy // ============================================================ public static TSource MaxBy( this IEnumerable source, Func keySelector) { return source.MaxBy(keySelector, null); } public static TSource MaxBy( this IEnumerable source, Func keySelector, IComparer comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); comparer ??= Comparer.Default; using IEnumerator enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) throw new InvalidOperationException("Sequence contains no elements."); TSource max = enumerator.Current; TKey maxKey = keySelector(max); while (enumerator.MoveNext()) { TSource current = enumerator.Current; TKey currentKey = keySelector(current); if (comparer.Compare(currentKey, maxKey) > 0) { max = current; maxKey = currentKey; } } return max; } public static TSource MaxBy( this IReadOnlyList source, Func keySelector) { return source.MaxBy(keySelector, null); } public static TSource MaxBy( this IReadOnlyList source, Func keySelector, IComparer comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); comparer ??= Comparer.Default; int count = source.Count; if (count == 0) throw new InvalidOperationException("Sequence contains no elements."); TSource max = source[0]; TKey maxKey = keySelector(max); for (int i = 1; i < count; i++) { TSource current = source[i]; TKey currentKey = keySelector(current); if (comparer.Compare(currentKey, maxKey) > 0) { max = current; maxKey = currentKey; } } return max; } // ============================================================ // IntersectBy // ============================================================ public static IEnumerable IntersectBy( this IEnumerable source, IEnumerable keys, Func keySelector) { return source.IntersectBy(keys, keySelector, null); } public static IEnumerable IntersectBy( this IEnumerable source, IEnumerable keys, Func keySelector, IEqualityComparer comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keys == null) throw new ArgumentNullException(nameof(keys)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); return IntersectByIterator(source, keys, keySelector, comparer); } private static IEnumerable IntersectByIterator( IEnumerable source, IEnumerable keys, Func keySelector, IEqualityComparer comparer) { var set = comparer == null ? new HashSet() : new HashSet(comparer); foreach (TKey key in keys) set.Add(key); foreach (TSource item in source) { if (set.Remove(keySelector(item))) yield return item; } } public static IEnumerable IntersectBy( this IReadOnlyList source, IEnumerable keys, Func keySelector) { return source.IntersectBy(keys, keySelector, null); } public static IEnumerable IntersectBy( this IReadOnlyList source, IEnumerable keys, Func keySelector, IEqualityComparer comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keys == null) throw new ArgumentNullException(nameof(keys)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); return IntersectByListIterator(source, keys, keySelector, comparer); } private static IEnumerable IntersectByListIterator( IReadOnlyList source, IEnumerable keys, Func keySelector, IEqualityComparer comparer) { var set = comparer == null ? new HashSet() : new HashSet(comparer); foreach (TKey key in keys) set.Add(key); int count = source.Count; for (int i = 0; i < count; i++) { TSource item = source[i]; if (set.Remove(keySelector(item))) yield return item; } } // ============================================================ // ExceptBy // ============================================================ public static IEnumerable ExceptBy( this IEnumerable source, IEnumerable keys, Func keySelector) { return source.ExceptBy(keys, keySelector, null); } public static IEnumerable ExceptBy( this IEnumerable source, IEnumerable keys, Func keySelector, IEqualityComparer comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keys == null) throw new ArgumentNullException(nameof(keys)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); return ExceptByIterator(source, keys, keySelector, comparer); } private static IEnumerable ExceptByIterator( IEnumerable source, IEnumerable keys, Func keySelector, IEqualityComparer comparer) { var set = comparer == null ? new HashSet() : new HashSet(comparer); foreach (TKey key in keys) set.Add(key); foreach (TSource item in source) { if (set.Add(keySelector(item))) yield return item; } } public static IEnumerable ExceptBy( this IReadOnlyList source, IEnumerable keys, Func keySelector) { return source.ExceptBy(keys, keySelector, null); } public static IEnumerable ExceptBy( this IReadOnlyList source, IEnumerable keys, Func keySelector, IEqualityComparer comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keys == null) throw new ArgumentNullException(nameof(keys)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); return ExceptByListIterator(source, keys, keySelector, comparer); } private static IEnumerable ExceptByListIterator( IReadOnlyList source, IEnumerable keys, Func keySelector, IEqualityComparer comparer) { var set = comparer == null ? new HashSet() : new HashSet(comparer); foreach (TKey key in keys) set.Add(key); int count = source.Count; for (int i = 0; i < count; i++) { TSource item = source[i]; if (set.Add(keySelector(item))) yield return item; } } // ========================= // DistinctBy // ========================= public static IEnumerable DistinctBy( this IEnumerable source, Func keySelector) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); var seen = new HashSet(); foreach (var item in source) { if (seen.Add(keySelector(item))) yield return item; } } public static IEnumerable DistinctBy( this IReadOnlyList source, Func keySelector) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); var seen = new HashSet(); for (int i = 0; i < source.Count; i++) { var item = source[i]; if (seen.Add(keySelector(item))) yield return item; } } // ========================= // Chunk // ========================= public static IEnumerable Chunk( this IEnumerable source, int size) { if (source == null) throw new ArgumentNullException(nameof(source)); if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size)); var buffer = new TSource[size]; int count = 0; foreach (var item in source) { buffer[count++] = item; if (count == size) { yield return buffer; buffer = new TSource[size]; count = 0; } } if (count > 0) { var last = new TSource[count]; Array.Copy(buffer, last, count); yield return last; } } public static IEnumerable Chunk( this IReadOnlyList source, int size) { if (source == null) throw new ArgumentNullException(nameof(source)); if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size)); int count = source.Count; for (int i = 0; i < count; i += size) { int chunkSize = Math.Min(size, count - i); var chunk = new TSource[chunkSize]; for (int j = 0; j < chunkSize; j++) chunk[j] = source[i + j]; yield return chunk; } } } } #endif