using System; using System.Collections.Generic; using System.Linq; using ICD.Common.Properties; using ICD.Common.Utils.Collections; namespace ICD.Common.Utils.Extensions { public static class EnumerableExtensions { /// /// Returns the first item in the sequence. Returns the provided default item if there /// are no elements in the sequence. /// /// /// /// /// public static T FirstOrDefault(this IEnumerable extends, T defaultItem) { if (extends == null) throw new ArgumentNullException("extends"); return extends.FirstOrDefault(i => true, defaultItem); } /// /// Returns the first element in the sequence matching the predicate. Returns the provided /// default item if there are no elements matching the predicate in the sequence. /// /// /// /// /// /// public static T FirstOrDefault(this IEnumerable extends, Func predicate, T defaultItem) { if (extends == null) throw new ArgumentNullException("extends"); if (predicate == null) throw new ArgumentNullException("predicate"); foreach (T item in extends.Where(predicate)) return item; return defaultItem; } /// /// Returns true if there is at least 1 item in the sequence. /// /// /// /// Outputs the first item in the sequence. /// public static bool TryFirst(this IEnumerable extends, out T item) { if (extends == null) throw new ArgumentNullException("extends"); return extends.TryFirst(i => true, out item); } /// /// Returns true if there is at least 1 item in the sequence. /// /// /// /// /// Outputs the first item in the sequence. /// public static bool TryFirst(this IEnumerable extends, Func predicate, out T item) { if (extends == null) throw new ArgumentNullException("extends"); if (predicate == null) throw new ArgumentNullException("predicate"); item = default(T); using (IEnumerator iterator = extends.GetEnumerator()) { while (iterator.MoveNext()) { if (!predicate(iterator.Current)) continue; item = iterator.Current; return true; } } return false; } /// /// Returns the true if an element with the given index is in the sequence. /// /// /// /// /// /// public static bool TryElementAt(this IEnumerable extends, int index, out T item) { if (extends == null) throw new ArgumentNullException("extends"); item = default(T); if (index < 0) return false; T[] itemArray = extends as T[] ?? extends.ToArray(); if (index >= itemArray.Length) return false; item = itemArray[index]; return true; } /// /// Compares two sequences for identical values, ignoring order. /// /// /// /// /// public static bool ScrambledEquals(this IEnumerable extends, IEnumerable other) { if (extends == null) throw new ArgumentNullException("extends"); if (extends == null) throw new ArgumentNullException("other"); return extends.ScrambledEquals(other, EqualityComparer.Default); } /// /// Compares two sequences for identical values, ignoring order. /// /// /// /// /// /// public static bool ScrambledEquals(this IEnumerable extends, IEnumerable other, IEqualityComparer comparer) { if (extends == null) throw new ArgumentNullException("extends"); if (extends == null) throw new ArgumentNullException("other"); if (comparer == null) throw new ArgumentNullException("comparer"); Dictionary count = new Dictionary(comparer); foreach (T item in extends) { if (count.ContainsKey(item)) count[item]++; else count.Add(item, 1); } foreach (T item in other) { if (count.ContainsKey(item)) count[item]--; else return false; } return count.Values.All(c => c == 0); } /// /// Returns the index that matches the predicate. /// /// /// /// /// public static int FindIndex(this IEnumerable extends, Predicate match) { if (extends == null) throw new ArgumentNullException("extends"); if (match == null) throw new ArgumentNullException("match"); int index = 0; foreach (T item in extends) { if (match(item)) return index; index++; } return -1; } /// /// Allows for selection of multiple results for each item in the sequence. /// /// /// /// /// /// public static IEnumerable SelectMulti(this IEnumerable extends, params Func[] selectors) { if (extends == null) throw new ArgumentNullException("extends"); if (selectors == null) throw new ArgumentNullException("selectors"); return extends.SelectMany(source => selectors, (source, selector) => selector(source)); } /// /// Enumerates each item in the sequence. /// Useful for processing a LINQ query without creating a new collection. /// /// /// [PublicAPI] public static void Execute(this IEnumerable extends) { if (extends == null) throw new ArgumentNullException("extends"); extends.ForEach(item => { }); } /// /// Performs the action for each item in the sequence. /// /// /// /// [PublicAPI] public static void ForEach(this IEnumerable extends, Action action) { if (extends == null) throw new ArgumentNullException("extends"); if (action == null) throw new ArgumentNullException("action"); extends.ForEach((item, index) => action(item)); } /// /// Performs the action for each item in the sequence. /// /// /// /// [PublicAPI] public static void ForEach(this IEnumerable extends, Action action) { if (extends == null) throw new ArgumentNullException("extends"); if (action == null) throw new ArgumentNullException("action"); int index = 0; foreach (T item in extends) action(item, index++); } #if SIMPLSHARP /// /// Prepends the item to the start of the sequence. /// /// /// /// /// public static IEnumerable Prepend(this IEnumerable extends, T item) { if (extends == null) throw new ArgumentNullException("extends"); return extends.PrependMany(new[] {item}); } #endif /// /// Prepends the items to the start of the sequence. /// /// /// /// /// [PublicAPI] public static IEnumerable PrependMany(this IEnumerable extends, params T[] items) { if (extends == null) throw new ArgumentNullException("extends"); if (items == null) throw new ArgumentNullException("items"); foreach (T item in items) yield return item; foreach (T each in extends) yield return each; } #if SIMPLSHARP /// /// Appends the item to the end of the sequence. /// /// /// /// /// public static IEnumerable Append(this IEnumerable extends, T item) { if (extends == null) throw new ArgumentNullException("extends"); return extends.AppendMany(new[] {item}); } #endif /// /// Appends the items to the end of the sequence. /// /// /// /// /// [PublicAPI] public static IEnumerable AppendMany(this IEnumerable extends, params T[] items) { if (extends == null) throw new ArgumentNullException("extends"); if (items == null) throw new ArgumentNullException("items"); foreach (T each in extends) yield return each; foreach (T item in items) yield return item; } /// /// Default ordering for the items in the sequence. /// /// /// /// public static IEnumerable Order(this IEnumerable extends) { if (extends == null) throw new ArgumentNullException("extends"); return extends.OrderBy(i => i); } /// /// Returns every item in the sequence except the given item. /// /// /// /// /// public static IEnumerable Except(this IEnumerable extends, T item) { if (extends == null) throw new ArgumentNullException("extends"); return extends.Except(new[] {item}); } /// /// Returns the sequence as a IcdHashSet. /// /// /// /// public static IcdHashSet ToHashSet(this IEnumerable extends) { if (extends == null) throw new ArgumentNullException("extends"); return new IcdHashSet(extends); } /// /// Returns the sequence as an index:value dictionary. /// /// /// /// [PublicAPI] public static Dictionary ToDictionary(this IEnumerable extends) { if (extends == null) throw new ArgumentNullException("extends"); Dictionary output = new Dictionary(); extends.ForEach((item, index) => output.Add(index, item)); return output; } /// /// Returns the sequence as an index:value dictionary. /// /// /// /// [PublicAPI] public static Dictionary ToDictionaryUInt(this IEnumerable extends) { if (extends == null) throw new ArgumentNullException("extends"); Dictionary output = new Dictionary(); extends.ForEach((item, index) => output.Add((uint)index, item)); return output; } /// /// Turns an enumerable of KeyValuePairs back into a dictionary /// /// /// /// /// public static Dictionary ToDictionary(this IEnumerable> extends) { if (extends == null) throw new ArgumentNullException("extends"); return extends.ToDictionary(x => x.Key, x => x.Value); } /// /// Returns other if the list is empty. /// Returns other if the list is non-empty and there are two different elements. /// Returns the element of the list if it is non-empty and all elements are the same. /// /// /// /// [PublicAPI] public static T Unanimous(this IEnumerable extends, T other) { if (extends == null) throw new ArgumentNullException("extends"); T[] array = extends as T[] ?? extends.ToArray(); return array.Unanimous() ? array.First() : other; } /// /// Returns false if the list is empty. /// Returns false if the list is non-empty and there are two different elements. /// Returns true if the list is non-empty and all elements are the same. /// /// /// [PublicAPI] public static bool Unanimous(this IEnumerable extends) { if (extends == null) throw new ArgumentNullException("extends"); T[] array = extends as T[] ?? extends.ToArray(); if (array.Length == 0) return false; T val = array.First(); return array.All(x => EqualityComparer.Default.Equals(x, val)); } /// /// Partitions a sequence into sequences of the given length. /// /// /// /// [PublicAPI] public static IEnumerable> Partition(this IEnumerable extends, int partitionSize) { if (extends == null) throw new ArgumentNullException("extends"); using (IEnumerator enumerator = extends.GetEnumerator()) { while (enumerator.MoveNext()) yield return YieldBatchElements(enumerator, partitionSize - 1); } } private static IEnumerable YieldBatchElements(IEnumerator source, int partitionSize) { if (source == null) throw new ArgumentNullException("source"); // Important to enumerate through partitionSize items before returning // Otherwise enumerable.Partition(3).Skip(1) will do unwanted things. List output = new List {source.Current}; for (int i = 0; i < partitionSize && source.MoveNext(); i++) output.Add(source.Current); return output; } /// /// Returns the minimal element of the given sequence, based on /// the given projection. /// /// /// If more than one element has the minimal projected value, the first /// one encountered will be returned. This overload uses the default comparer /// for the projected type. This operator uses immediate execution, but /// only buffers a single result (the current minimal element). /// /// Type of the source sequence /// Type of the projected element /// Source sequence /// Selector to use to pick the results to compare /// The minimal element, according to the projection. /// or is null /// is empty [PublicAPI] public static TSource MinBy(this IEnumerable source, Func selector) { if (source == null) throw new ArgumentNullException("source"); if (selector == null) throw new ArgumentNullException("selector"); return source.MinBy(selector, Comparer.Default); } /// /// Returns the minimal element of the given sequence, based on /// the given projection and the specified comparer for projected values. /// /// /// If more than one element has the minimal projected value, the first /// one encountered will be returned. This operator uses immediate execution, but /// only buffers a single result (the current minimal element). /// /// Type of the source sequence /// Type of the projected element /// Source sequence /// Selector to use to pick the results to compare /// Comparer to use to compare projected values /// The minimal element, according to the projection. /// , /// or is null /// is empty [PublicAPI] public static TSource MinBy(this IEnumerable source, Func selector, IComparer comparer) { if (source == null) throw new ArgumentNullException("source"); if (selector == null) throw new ArgumentNullException("selector"); if (comparer == null) throw new ArgumentNullException("comparer"); using (IEnumerator sourceIterator = source.GetEnumerator()) { if (!sourceIterator.MoveNext()) throw new InvalidOperationException("Sequence contains no elements"); TSource min = sourceIterator.Current; TKey minKey = selector(min); while (sourceIterator.MoveNext()) { TSource candidate = sourceIterator.Current; TKey candidateProjected = selector(candidate); if (comparer.Compare(candidateProjected, minKey) >= 0) continue; min = candidate; minKey = candidateProjected; } return min; } } /// /// Removes any null elements from an enumerable of nullable value types /// /// /// /// [PublicAPI] public static IEnumerable ExceptNulls(this IEnumerable extends) where T : struct { if (extends == null) throw new ArgumentNullException("extends"); return extends.Where(e => e.HasValue).Select(e => e.Value); } /// /// Computes the sum of a sequence of byte values. /// /// /// [PublicAPI] public static byte Sum(this IEnumerable extends) { if (extends == null) throw new ArgumentNullException("extends"); return (byte)extends.Select(i => (int)i).Sum(); } /// /// Skips duplicate, consecutive items. /// E.g. /// [1, 2, 2, 3, 1, 1] /// Becomes /// [1, 2, 3, 1] /// /// /// /// [PublicAPI] public static IEnumerable Consolidate(this IEnumerable extends) { if (extends == null) throw new ArgumentNullException("extends"); return Consolidate(extends, Comparer.Default); } /// /// Skips duplicate, consecutive items. /// E.g. /// [1, 2, 2, 3, 1, 1] /// Becomes /// [1, 2, 3, 1] /// /// /// /// /// [PublicAPI] public static IEnumerable Consolidate(this IEnumerable extends, IComparer comparer) { if (extends == null) throw new ArgumentNullException("extends"); if (comparer == null) throw new ArgumentNullException("comparer"); bool first = true; T last = default(T); foreach (T item in extends) { if (!first && comparer.Compare(last, item) == 0) continue; first = false; last = item; yield return item; } } /// /// Returns true if all items in the sequence match the predicate. /// Returns false if the sequence is empty. /// /// /// /// /// [PublicAPI] public static bool AnyAndAll(this IEnumerable extends, Func predicate) { if (extends == null) throw new ArgumentNullException("extends"); if (predicate == null) throw new ArgumentNullException("predicate"); bool any = false; foreach (T item in extends) { any = true; if (!predicate(item)) return false; } return any; } } }