using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using Umbraco.Core.Logging; namespace Umbraco.Core { /// /// Extensions for enumerable sources /// public static class EnumerableExtensions { public static IEnumerable> InGroupsOf(this IEnumerable source, int groupSize) { if (source == null) throw new ArgumentNullException("source"); if (groupSize <= 0) throw new ArgumentException("Must be greater than zero.", "groupSize"); return source .Select((x, i) => Tuple.Create(i / groupSize, x)) .GroupBy(t => t.Item1, t => t.Item2); } /// The distinct by. /// The source. /// The key selector. /// Source type /// Key type /// the unique list public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) where TKey : IEquatable { return source.Distinct(DelegateEqualityComparer.CompareMember(keySelector)); } /// /// Returns a sequence of length whose elements are the result of invoking . /// /// /// The factory. /// The count. /// public static IEnumerable Range(Func factory, int count) { for (int i = 1; i <= count; i++) { yield return factory.Invoke(i - 1); } } /// The if not null. /// The items. /// The action. /// The type public static void IfNotNull(this IEnumerable items, Action action) where TItem : class { if (items != null) { foreach (TItem item in items) { item.IfNotNull(action); } } } /// The for each. /// The items. /// The func. /// item type /// Result type /// the Results public static TResult[] ForEach(this IEnumerable items, Func func) { return items.Select(func).ToArray(); } /// The for each. /// The items. /// The action. /// Item type /// list of TItem public static IEnumerable ForEach(this IEnumerable items, Action action) { if (items != null) { foreach (TItem item in items) { action(item); } } return items; } /// The flatten list. /// The items. /// The select child. /// Item type /// list of TItem [Obsolete("Do not use, use SelectRecursive instead which has far less potential of re-iterating an iterator which may cause significantly more SQL queries")] public static IEnumerable FlattenList(this IEnumerable e, Func> f) { return e.SelectMany(c => f(c).FlattenList(f)).Concat(e); } /// /// Returns true if all items in the other collection exist in this collection /// /// /// /// /// public static bool ContainsAll(this IEnumerable source, IEnumerable other) { return other.Except(source).Any() == false; } /// /// Returns true if the source contains any of the items in the other list /// /// /// /// /// public static bool ContainsAny(this IEnumerable source, IEnumerable other) { return other.Any(source.Contains); } /// /// Removes all matching items from an . /// /// /// The list. /// The predicate. /// public static void RemoveAll(this IList list, Func predicate) { for (var i = 0; i < list.Count; i++) { if (predicate(list[i])) { list.RemoveAt(i--); } } } /// /// Removes all matching items from an . /// /// /// The list. /// The predicate. /// public static void RemoveAll(this ICollection list, Func predicate) { var matches = list.Where(predicate).ToArray(); foreach (var match in matches) { list.Remove(match); } } public static IEnumerable SelectRecursive( this IEnumerable source, Func> recursiveSelector, int maxRecusionDepth = 100) { var stack = new Stack>(); stack.Push(source.GetEnumerator()); try { while (stack.Count > 0) { if (stack.Count > maxRecusionDepth) throw new InvalidOperationException("Maximum recursion depth reached of " + maxRecusionDepth); if (stack.Peek().MoveNext()) { var current = stack.Peek().Current; yield return current; stack.Push(recursiveSelector(current).GetEnumerator()); } else { stack.Pop().Dispose(); } } } finally { while (stack.Count > 0) { stack.Pop().Dispose(); } } } /// /// Filters a sequence of values to ignore those which are null. /// /// /// The coll. /// /// public static IEnumerable WhereNotNull(this IEnumerable coll) where T : class { return coll.Where(x => x != null); } public static IEnumerable ForAllThatAre(this IEnumerable sequence, Action projection) where TActual : class { return sequence.Select( x => { if (x is TActual) { var casted = x as TActual; projection.Invoke(casted); } return x; }); } /// /// Finds the index of the first item matching an expression in an enumerable. /// /// The type of the enumerated objects. /// The enumerable to search. /// The expression to test the items against. /// The index of the first matching item, or -1. public static int FindIndex(this IEnumerable items, Func predicate) { return FindIndex(items, 0, predicate); } /// /// Finds the index of the first item matching an expression in an enumerable. /// /// The type of the enumerated objects. /// The enumerable to search. /// The index to start at. /// The expression to test the items against. /// The index of the first matching item, or -1. public static int FindIndex(this IEnumerable items, int startIndex, Func predicate) { if (items == null) throw new ArgumentNullException("items"); if (predicate == null) throw new ArgumentNullException("predicate"); if (startIndex < 0) throw new ArgumentOutOfRangeException("startIndex"); var index = startIndex; if (index > 0) items = items.Skip(index); foreach (var item in items) { if (predicate(item)) return index; index++; } return -1; } ///Finds the index of the first occurence of an item in an enumerable. ///The enumerable to search. ///The item to find. ///The index of the first matching item, or -1 if the item was not found. public static int IndexOf(this IEnumerable items, T item) { return items.FindIndex(i => EqualityComparer.Default.Equals(item, i)); } /// /// Determines if 2 lists have equal elements within them regardless of how they are sorted /// /// /// /// /// /// /// The logic for this is taken from: /// http://stackoverflow.com/questions/4576723/test-whether-two-ienumerablet-have-the-same-values-with-the-same-frequencies /// /// There's a few answers, this one seems the best for it's simplicity and based on the comment of Eamon /// public static bool UnsortedSequenceEqual(this IEnumerable source, IEnumerable other) { if (source == null && other == null) return true; if (source == null || other == null) return false; var list1Groups = source.ToLookup(i => i); var list2Groups = other.ToLookup(i => i); return list1Groups.Count == list2Groups.Count && list1Groups.All(g => g.Count() == list2Groups[g.Key].Count()); } } }