#nullable enable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; namespace ICSharpCode.Decompiler.Util { static class CollectionExtensions { public static void Deconstruct(this KeyValuePair pair, out K key, out V value) { key = pair.Key; value = pair.Value; } #if !NETCORE public static IEnumerable<(A, B)> Zip(this IEnumerable input1, IEnumerable input2) { return input1.Zip(input2, (a, b) => (a, b)); } #endif public static IEnumerable<(A?, B?)> ZipLongest(this IEnumerable input1, IEnumerable input2) { using (var it1 = input1.GetEnumerator()) { using (var it2 = input2.GetEnumerator()) { bool hasElements1 = true; bool hasElements2 = true; while (true) { if (hasElements1) hasElements1 = it1.MoveNext(); if (hasElements2) hasElements2 = it2.MoveNext(); if (!(hasElements1 || hasElements2)) break; yield return ((hasElements1 ? it1.Current : default), (hasElements2 ? it2.Current : default)); } } } } public static IEnumerable Slice(this IReadOnlyList input, int offset, int length) { for (int i = offset; i < offset + length; i++) { yield return input[i]; } } public static IEnumerable Slice(this IReadOnlyList input, int offset) { int length = input.Count; for (int i = offset; i < length; i++) { yield return input[i]; } } #if !NETCORE public static HashSet ToHashSet(this IEnumerable input) { return new HashSet(input); } #endif public static IEnumerable SkipLast(this IReadOnlyCollection input, int count) { return input.Take(input.Count - count); } public static IEnumerable TakeLast(this IReadOnlyCollection input, int count) { return input.Skip(input.Count - count); } public static T? PopOrDefault(this Stack stack) { if (stack.Count == 0) return default(T); return stack.Pop(); } public static T? PeekOrDefault(this Stack stack) { if (stack.Count == 0) return default(T); return stack.Peek(); } public static int MaxOrDefault(this IEnumerable input, Func selector, int defaultValue = 0) { int max = defaultValue; foreach (var element in input) { int value = selector(element); if (value > max) max = value; } return max; } public static int IndexOf(this IReadOnlyList collection, T value) { var comparer = EqualityComparer.Default; int index = 0; foreach (T item in collection) { if (comparer.Equals(item, value)) { return index; } index++; } return -1; } public static void AddRange(this ICollection collection, IEnumerable input) { foreach (T item in input) collection.Add(item); } /// /// Equivalent to collection.Select(func).ToArray(), but more efficient as it makes /// use of the input collection's known size. /// public static U[] SelectArray(this ICollection collection, Func func) { U[] result = new U[collection.Count]; int index = 0; foreach (var element in collection) { result[index++] = func(element); } return result; } /// /// Equivalent to collection.Select(func).ToImmutableArray(), but more efficient as it makes /// use of the input collection's known size. /// public static ImmutableArray SelectImmutableArray(this IReadOnlyCollection collection, Func func) { var builder = ImmutableArray.CreateBuilder(collection.Count); foreach (var element in collection) { builder.Add(func(element)); } return builder.MoveToImmutable(); } /// /// Equivalent to collection.Select(func).ToArray(), but more efficient as it makes /// use of the input collection's known size. /// public static U[] SelectReadOnlyArray(this IReadOnlyCollection collection, Func func) { U[] result = new U[collection.Count]; int index = 0; foreach (var element in collection) { result[index++] = func(element); } return result; } /// /// Equivalent to collection.Select(func).ToArray(), but more efficient as it makes /// use of the input collection's known size. /// public static U[] SelectArray(this List collection, Func func) { U[] result = new U[collection.Count]; int index = 0; foreach (var element in collection) { result[index++] = func(element); } return result; } /// /// Equivalent to collection.Select(func).ToArray(), but more efficient as it makes /// use of the input collection's known size. /// public static U[] SelectArray(this T[] collection, Func func) { U[] result = new U[collection.Length]; int index = 0; foreach (var element in collection) { result[index++] = func(element); } return result; } /// /// Equivalent to collection.Select(func).ToList(), but more efficient as it makes /// use of the input collection's known size. /// public static List SelectList(this ICollection collection, Func func) { List result = new List(collection.Count); foreach (var element in collection) { result.Add(func(element)); } return result; } public static IEnumerable SelectWithIndex(this IEnumerable source, Func func) { int index = 0; foreach (var element in source) yield return func(index++, element); } public static IEnumerable<(int, T)> WithIndex(this ICollection source) { int index = 0; foreach (var item in source) { yield return (index, item); index++; } } /// /// The merge step of merge sort. /// public static IEnumerable Merge(this IEnumerable input1, IEnumerable input2, Comparison comparison) { using (var enumA = input1.GetEnumerator()) using (var enumB = input2.GetEnumerator()) { bool moreA = enumA.MoveNext(); bool moreB = enumB.MoveNext(); while (moreA && moreB) { if (comparison(enumA.Current, enumB.Current) <= 0) { yield return enumA.Current; moreA = enumA.MoveNext(); } else { yield return enumB.Current; moreB = enumB.MoveNext(); } } while (moreA) { yield return enumA.Current; moreA = enumA.MoveNext(); } while (moreB) { yield return enumB.Current; moreB = enumB.MoveNext(); } } } /// /// Returns the minimum element. /// /// The input sequence is empty public static T MinBy(this IEnumerable source, Func keySelector) where K : IComparable { return source.MinBy(keySelector, Comparer.Default); } /// /// Returns the minimum element. /// /// The input sequence is empty public static T MinBy(this IEnumerable source, Func keySelector, IComparer? keyComparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); if (keyComparer == null) keyComparer = Comparer.Default; using (var enumerator = source.GetEnumerator()) { if (!enumerator.MoveNext()) throw new InvalidOperationException("Sequence contains no elements"); T minElement = enumerator.Current; K minKey = keySelector(minElement); while (enumerator.MoveNext()) { T element = enumerator.Current; K key = keySelector(element); if (keyComparer.Compare(key, minKey) < 0) { minElement = element; minKey = key; } } return minElement; } } /// /// Returns the maximum element. /// /// The input sequence is empty public static T MaxBy(this IEnumerable source, Func keySelector) where K : IComparable { return source.MaxBy(keySelector, Comparer.Default); } /// /// Returns the maximum element. /// /// The input sequence is empty public static T MaxBy(this IEnumerable source, Func keySelector, IComparer? keyComparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); if (keyComparer == null) keyComparer = Comparer.Default; using (var enumerator = source.GetEnumerator()) { if (!enumerator.MoveNext()) throw new InvalidOperationException("Sequence contains no elements"); T maxElement = enumerator.Current; K maxKey = keySelector(maxElement); while (enumerator.MoveNext()) { T element = enumerator.Current; K key = keySelector(element); if (keyComparer.Compare(key, maxKey) > 0) { maxElement = element; maxKey = key; } } return maxElement; } } public static void RemoveLast(this IList list) { if (list == null) throw new ArgumentNullException(nameof(list)); list.RemoveAt(list.Count - 1); } public static T? OnlyOrDefault(this IEnumerable source, Func predicate) => OnlyOrDefault(source.Where(predicate)); public static T? OnlyOrDefault(this IEnumerable source) { bool any = false; T? first = default; foreach (var t in source) { if (any) return default(T); first = t; any = true; } return first; } #if !NETCORE public static int EnsureCapacity(this List list, int capacity) { if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity)); if (list.Capacity < capacity) { const int DefaultCapacity = 4; const int MaxLength = 0X7FFFFFC7; int newcapacity = list.Capacity == 0 ? DefaultCapacity : 2 * list.Capacity; if ((uint)newcapacity > MaxLength) newcapacity = MaxLength; if (newcapacity < capacity) newcapacity = capacity; list.Capacity = newcapacity; } return list.Capacity; } #endif #region Aliases/shortcuts for Enumerable extension methods public static bool Any(this ICollection list) => list.Count > 0; public static bool Any(this T[] array, Predicate match) => Array.Exists(array, match); public static bool Any(this List list, Predicate match) => list.Exists(match); public static bool All(this T[] array, Predicate match) => Array.TrueForAll(array, match); public static bool All(this List list, Predicate match) => list.TrueForAll(match); public static T? FirstOrDefault(this T[] array, Predicate predicate) => Array.Find(array, predicate); public static T? FirstOrDefault(this List list, Predicate predicate) => list.Find(predicate); public static T Last(this IList list) => list[list.Count - 1]; #endregion } }