using System; using System.Collections.Generic; using System.Linq; using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core { /// /// Extensions for enumerable sources /// public static class EnumerableExtensions { /// /// Wraps this object instance into an IEnumerable{T} consisting of a single item. /// /// Type of the object. /// The instance that will be wrapped. /// An IEnumerable{T} consisting of a single item. public static IEnumerable Yield(this T item) { // see EnumeratorBenchmarks - this is faster, and allocates less, than returning an array yield return item; } 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"); // following code derived from MoreLinq and does not allocate bazillions of tuples T[] temp = null; var count = 0; foreach (var item in source) { if (temp == null) temp = new T[groupSize]; temp[count++] = item; if (count != groupSize) continue; yield return temp/*.Select(x => x)*/; temp = null; count = 0; } if (temp != null && count > 0) yield return temp.Take(count); } public static IEnumerable SelectByGroups(this IEnumerable source, Func, IEnumerable> selector, int groupSize) { // don't want to use a SelectMany(x => x) here - isn't this better? // ReSharper disable once LoopCanBeConvertedToQuery foreach (var resultGroup in source.InGroupsOf(groupSize).Select(selector)) foreach (var result in resultGroup) yield return result; } /// 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); } } } /// /// Returns true if all items in the other collection exist in this collection /// /// /// /// /// public static bool ContainsAll(this IEnumerable source, IEnumerable other) { if (source == null) throw new ArgumentNullException("source"); if (other == null) throw new ArgumentNullException("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 occurrence 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()); } /// /// Transforms an enumerable. /// /// /// /// /// public static IEnumerable Transform(this IEnumerable source, Func, IEnumerable> transform) { return transform(source); } /// /// Gets a null IEnumerable as an empty IEnumerable. /// /// /// /// public static IEnumerable EmptyNull(this IEnumerable items) { return items ?? Enumerable.Empty(); } // the .OfType() filter is nice when there's only one type // this is to support filtering with multiple types public static IEnumerable OfTypes(this IEnumerable contents, params Type[] types) { return contents.Where(x => types.Contains(x.GetType())); } public static IEnumerable SkipLast(this IEnumerable source) { using (var e = source.GetEnumerator()) { if (e.MoveNext() == false) yield break; for (var value = e.Current; e.MoveNext(); value = e.Current) yield return value; } } public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector, Direction sortOrder) { return sortOrder == Direction.Ascending ? source.OrderBy(keySelector) : source.OrderByDescending(keySelector); } } }