// // Enumerable.cs // // Authors: // Marek Safar (marek.safar@gmail.com) // Antonello Provenzano // Alejandro Serrano "Serras" (trupill@yahoo.es) // Jb Evain (jbevain@novell.com) // // Copyright (C) 2007 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // precious: http://www.hookedonlinq.com using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; namespace System.Linq { public static class Enumerable { enum Fallback { Default, Throw } static class PredicateOf { public static readonly Func Always = (t) => true; } static class Function { public static readonly Func Identity = (t) => t; } static class EmptyOf { public static readonly T[] Instance = new T [0]; } static class ReadOnlyCollectionOf { public static readonly ReadOnlyCollection Empty = new ReadOnlyCollection (EmptyOf.Instance); } #region Aggregate public static TSource Aggregate (this IEnumerable source, Func func) { Check.SourceAndFunc (source, func); // custom foreach so that we can efficiently throw an exception // if zero elements and treat the first element differently using (var enumerator = source.GetEnumerator ()) { if (!enumerator.MoveNext ()) throw new InvalidOperationException ("No elements in source list"); TSource folded = enumerator.Current; while (enumerator.MoveNext ()) folded = func (folded, enumerator.Current); return folded; } } public static TAccumulate Aggregate (this IEnumerable source, TAccumulate seed, Func func) { Check.SourceAndFunc (source, func); TAccumulate folded = seed; foreach (TSource element in source) folded = func (folded, element); return folded; } public static TResult Aggregate (this IEnumerable source, TAccumulate seed, Func func, Func resultSelector) { Check.SourceAndFunc (source, func); if (resultSelector == null) throw new ArgumentNullException ("resultSelector"); var result = seed; foreach (var e in source) result = func (result, e); return resultSelector (result); } #endregion #region All public static bool All (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); foreach (var element in source) if (!predicate (element)) return false; return true; } #endregion #region Any public static bool Any (this IEnumerable source) { Check.Source (source); var collection = source as ICollection; if (collection != null) return collection.Count > 0; using (var enumerator = source.GetEnumerator ()) return enumerator.MoveNext (); } public static bool Any (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); foreach (TSource element in source) if (predicate (element)) return true; return false; } #endregion #region AsEnumerable public static IEnumerable AsEnumerable (this IEnumerable source) { return source; } #endregion #region Average public static double Average (this IEnumerable source) { Check.Source (source); long total = 0; int count = 0; foreach (var element in source){ total = checked (total + element); count++; } if (count == 0) throw new InvalidOperationException (); return total / (double) count; } public static double Average (this IEnumerable source) { Check.Source (source); long total = 0; long count = 0; foreach (var element in source){ total += element; count++; } if (count == 0) throw new InvalidOperationException (); return total / (double) count; } public static double Average (this IEnumerable source) { Check.Source (source); double total = 0; long count = 0; foreach (var element in source){ total += element; count++; } if (count == 0) throw new InvalidOperationException (); return total / count; } public static float Average (this IEnumerable source) { Check.Source (source); float total = 0; long count = 0; foreach (var element in source){ total += element; count++; } if (count == 0) throw new InvalidOperationException (); return total / count; } public static decimal Average (this IEnumerable source) { Check.Source (source); decimal total = 0; long count = 0; foreach (var element in source){ total += element; count++; } if (count == 0) throw new InvalidOperationException (); return total / count; } static TResult? AverageNullable (this IEnumerable source, Func func, Func result) where TElement : struct where TAggregate : struct where TResult : struct { Check.Source (source); var total = default (TAggregate); long counter = 0; foreach (var element in source) { if (!element.HasValue) continue; total = func (total, element.Value); counter++; } if (counter == 0) return null; return new TResult? (result (total, counter)); } public static double? Average (this IEnumerable source) { Check.Source (source); long total = 0; long counter = 0; foreach (var element in source) { if (!element.HasValue) continue; total = total + element.Value; counter++; } if (counter == 0) return null; return new double? (total / (double) counter); } public static double? Average (this IEnumerable source) { Check.Source (source); long total = 0; long counter = 0; foreach (var element in source) { if (!element.HasValue) continue; total = checked (total + element.Value); counter++; } if (counter == 0) return null; return new double? (total / (double) counter); } public static double? Average (this IEnumerable source) { Check.Source (source); double total = 0; long counter = 0; foreach (var element in source) { if (!element.HasValue) continue; total = total + element.Value; counter++; } if (counter == 0) return null; return new double? (total / counter); } public static decimal? Average (this IEnumerable source) { Check.Source (source); decimal total = 0; long counter = 0; foreach (var element in source) { if (!element.HasValue) continue; total = total + element.Value; counter++; } if (counter == 0) return null; return new decimal? (total / counter); } public static float? Average (this IEnumerable source) { Check.Source (source); float total = 0; long counter = 0; foreach (var element in source) { if (!element.HasValue) continue; total = total + element.Value; counter++; } if (counter == 0) return null; return new float? (total / counter); } public static double Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); long total = 0; long count = 0; foreach (var element in source){ total += selector (element); count++; } if (count == 0) throw new InvalidOperationException (); return total / (double) count; } public static double? Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); long total = 0; long counter = 0; foreach (var element in source) { var value = selector (element); if (!value.HasValue) continue; total = total + value.Value; counter++; } if (counter == 0) return null; return new double? (total / (double) counter); } public static double Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); long total = 0; long count = 0; foreach (var element in source){ total = checked (total + selector (element)); count++; } if (count == 0) throw new InvalidOperationException (); return total / (double) count; } public static double? Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); long total = 0; long counter = 0; foreach (var element in source) { var value = selector (element); if (!value.HasValue) continue; total = checked (total + value.Value); counter++; } if (counter == 0) return null; return new double? (total / (double) counter); } public static double Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); double total = 0; long count = 0; foreach (var element in source){ total += selector (element); count++; } if (count == 0) throw new InvalidOperationException (); return total / count; } public static double? Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); double total = 0; long counter = 0; foreach (var element in source) { var value = selector (element); if (!value.HasValue) continue; total = total + value.Value; counter++; } if (counter == 0) return null; return new double? (total / counter); } public static float Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); float total = 0; long count = 0; foreach (var element in source){ total += selector (element); count++; } if (count == 0) throw new InvalidOperationException (); return total / count; } public static float? Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); float total = 0; long counter = 0; foreach (var element in source) { var value = selector (element); if (!value.HasValue) continue; total = total + value.Value; counter++; } if (counter == 0) return null; return new float? (total / counter); } public static decimal Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); decimal total = 0; long count = 0; foreach (var element in source){ total += selector (element); count++; } if (count == 0) throw new InvalidOperationException (); return total / count; } public static decimal? Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); decimal total = 0; long counter = 0; foreach (var element in source) { var value = selector (element); if (!value.HasValue) continue; total = total + value.Value; counter++; } if (counter == 0) return null; return new decimal? (total / counter); } #endregion #region Cast public static IEnumerable Cast (this IEnumerable source) { Check.Source (source); var actual = source as IEnumerable; if (actual != null) return actual; return CreateCastIterator (source); } static IEnumerable CreateCastIterator (IEnumerable source) { foreach (TResult element in source) yield return element; } #endregion #region Concat public static IEnumerable Concat (this IEnumerable first, IEnumerable second) { Check.FirstAndSecond (first, second); return CreateConcatIterator (first, second); } static IEnumerable CreateConcatIterator (IEnumerable first, IEnumerable second) { foreach (TSource element in first) yield return element; foreach (TSource element in second) yield return element; } #endregion #region Contains public static bool Contains (this IEnumerable source, TSource value) { var collection = source as ICollection; if (collection != null) return collection.Contains (value); return Contains (source, value, null); } public static bool Contains (this IEnumerable source, TSource value, IEqualityComparer comparer) { Check.Source (source); if (comparer == null) comparer = EqualityComparer.Default; foreach (var element in source) if (comparer.Equals (element, value)) return true; return false; } #endregion #region Count public static int Count (this IEnumerable source) { Check.Source (source); var collection = source as ICollection; if (collection != null) return collection.Count; int counter = 0; using (var enumerator = source.GetEnumerator ()) while (enumerator.MoveNext ()) checked { counter++; } return counter; } public static int Count (this IEnumerable source, Func predicate) { Check.SourceAndSelector (source, predicate); int counter = 0; foreach (var element in source) if (predicate (element)) checked { counter++; } return counter; } #endregion #region DefaultIfEmpty public static IEnumerable DefaultIfEmpty (this IEnumerable source) { return DefaultIfEmpty (source, default (TSource)); } public static IEnumerable DefaultIfEmpty (this IEnumerable source, TSource defaultValue) { Check.Source (source); return CreateDefaultIfEmptyIterator (source, defaultValue); } static IEnumerable CreateDefaultIfEmptyIterator (IEnumerable source, TSource defaultValue) { bool empty = true; foreach (TSource item in source) { empty = false; yield return item; } if (empty) yield return defaultValue; } #endregion #region Distinct public static IEnumerable Distinct (this IEnumerable source) { return Distinct (source, null); } public static IEnumerable Distinct (this IEnumerable source, IEqualityComparer comparer) { Check.Source (source); if (comparer == null) comparer = EqualityComparer.Default; return CreateDistinctIterator (source, comparer); } static IEnumerable CreateDistinctIterator (IEnumerable source, IEqualityComparer comparer) { var items = new HashSet (comparer); foreach (var element in source) { if (! items.Contains (element)) { items.Add (element); yield return element; } } } #endregion #region ElementAt static TSource ElementAt (this IEnumerable source, int index, Fallback fallback) { long counter = 0L; foreach (var element in source) { if (index == counter++) return element; } if (fallback == Fallback.Throw) throw new ArgumentOutOfRangeException (); return default (TSource); } public static TSource ElementAt (this IEnumerable source, int index) { Check.Source (source); if (index < 0) throw new ArgumentOutOfRangeException (); var list = source as IList; if (list != null) return list [index]; return source.ElementAt (index, Fallback.Throw); } #endregion #region ElementAtOrDefault public static TSource ElementAtOrDefault (this IEnumerable source, int index) { Check.Source (source); if (index < 0) return default (TSource); var list = source as IList; if (list != null) return index < list.Count ? list [index] : default (TSource); return source.ElementAt (index, Fallback.Default); } #endregion #region Empty public static IEnumerable Empty () { return EmptyOf.Instance; } #endregion #region Except public static IEnumerable Except (this IEnumerable first, IEnumerable second) { return Except (first, second, null); } public static IEnumerable Except (this IEnumerable first, IEnumerable second, IEqualityComparer comparer) { Check.FirstAndSecond (first, second); if (comparer == null) comparer = EqualityComparer.Default; return CreateExceptIterator (first, second, comparer); } static IEnumerable CreateExceptIterator (IEnumerable first, IEnumerable second, IEqualityComparer comparer) { var items = new HashSet (second, comparer); foreach (var element in first) { if (items.Add (element)) yield return element; } } #endregion #region First static TSource First (this IEnumerable source, Func predicate, Fallback fallback) { foreach (var element in source) if (predicate (element)) return element; if (fallback == Fallback.Throw) throw new InvalidOperationException (); return default (TSource); } public static TSource First (this IEnumerable source) { Check.Source (source); var list = source as IList; if (list != null) { if (list.Count != 0) return list [0]; } else { using (var enumerator = source.GetEnumerator ()) { if (enumerator.MoveNext ()) return enumerator.Current; } } throw new InvalidOperationException ("The source sequence is empty"); } public static TSource First (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); return source.First (predicate, Fallback.Throw); } #endregion #region FirstOrDefault public static TSource FirstOrDefault (this IEnumerable source) { Check.Source (source); return source.First (PredicateOf.Always, Fallback.Default); } public static TSource FirstOrDefault (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); return source.First (predicate, Fallback.Default); } #endregion #region GroupBy private static List ContainsGroup ( Dictionary> items, K key, IEqualityComparer comparer) { IEqualityComparer comparerInUse = (comparer ?? EqualityComparer.Default); foreach (KeyValuePair> value in items) { if (comparerInUse.Equals (value.Key, key)) return value.Value; } return null; } public static IEnumerable> GroupBy (this IEnumerable source, Func keySelector) { return GroupBy (source, keySelector, null); } public static IEnumerable> GroupBy (this IEnumerable source, Func keySelector, IEqualityComparer comparer) { Check.SourceAndKeySelector (source, keySelector); return CreateGroupByIterator (source, keySelector, comparer); } static IEnumerable> CreateGroupByIterator (this IEnumerable source, Func keySelector, IEqualityComparer comparer) { var groups = new Dictionary> (); var nullList = new List (); int counter = 0; int nullCounter = -1; foreach (TSource element in source) { TKey key = keySelector (element); if (key == null) { nullList.Add (element); if (nullCounter == -1) { nullCounter = counter; counter++; } } else { List group = ContainsGroup (groups, key, comparer); if (group == null) { group = new List (); groups.Add (key, group); counter++; } group.Add (element); } } counter = 0; foreach (var group in groups) { if (counter == nullCounter) { yield return new Grouping (default (TKey), nullList); counter++; } yield return new Grouping (group.Key, group.Value); counter++; } if (counter == nullCounter) { yield return new Grouping (default (TKey), nullList); counter++; } } public static IEnumerable> GroupBy (this IEnumerable source, Func keySelector, Func elementSelector) { return GroupBy (source, keySelector, elementSelector, null); } public static IEnumerable> GroupBy (this IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer) { Check.SourceAndKeyElementSelectors (source, keySelector, elementSelector); return CreateGroupByIterator (source, keySelector, elementSelector, comparer); } static IEnumerable> CreateGroupByIterator (this IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer) { var groups = new Dictionary> (); var nullList = new List (); int counter = 0; int nullCounter = -1; foreach (TSource item in source) { TKey key = keySelector (item); TElement element = elementSelector (item); if (key == null) { nullList.Add (element); if (nullCounter == -1) { nullCounter = counter; counter++; } } else { List group = ContainsGroup (groups, key, comparer); if (group == null) { group = new List (); groups.Add (key, group); counter++; } group.Add (element); } } counter = 0; foreach (var group in groups) { if (counter == nullCounter) { yield return new Grouping (default (TKey), nullList); counter++; } yield return new Grouping (group.Key, group.Value); counter++; } if (counter == nullCounter) { yield return new Grouping (default (TKey), nullList); counter++; } } public static IEnumerable GroupBy (this IEnumerable source, Func keySelector, Func elementSelector, Func, TResult> resultSelector) { return GroupBy (source, keySelector, elementSelector, resultSelector, null); } public static IEnumerable GroupBy (this IEnumerable source, Func keySelector, Func elementSelector, Func, TResult> resultSelector, IEqualityComparer comparer) { Check.GroupBySelectors (source, keySelector, elementSelector, resultSelector); return CreateGroupByIterator (source, keySelector, elementSelector, resultSelector, comparer); } static IEnumerable CreateGroupByIterator (this IEnumerable source, Func keySelector, Func elementSelector, Func, TResult> resultSelector, IEqualityComparer comparer) { IEnumerable> groups = GroupBy ( source, keySelector, elementSelector, comparer); foreach (IGrouping group in groups) yield return resultSelector (group.Key, group); } public static IEnumerable GroupBy (this IEnumerable source, Func keySelector, Func, TResult> resultSelector) { return GroupBy (source, keySelector, resultSelector, null); } public static IEnumerable GroupBy (this IEnumerable source, Func keySelector, Func, TResult> resultSelector, IEqualityComparer comparer) { Check.SourceAndKeyResultSelectors (source, keySelector, resultSelector); return CreateGroupByIterator (source, keySelector, resultSelector, comparer); } static IEnumerable CreateGroupByIterator (this IEnumerable source, Func keySelector, Func, TResult> resultSelector, IEqualityComparer comparer) { IEnumerable> groups = GroupBy (source, keySelector, comparer); foreach (IGrouping group in groups) yield return resultSelector (group.Key, group); } #endregion # region GroupJoin public static IEnumerable GroupJoin (this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func, TResult> resultSelector) { return GroupJoin (outer, inner, outerKeySelector, innerKeySelector, resultSelector, null); } public static IEnumerable GroupJoin (this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func, TResult> resultSelector, IEqualityComparer comparer) { Check.JoinSelectors (outer, inner, outerKeySelector, innerKeySelector, resultSelector); if (comparer == null) comparer = EqualityComparer.Default; return CreateGroupJoinIterator (outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer); } static IEnumerable CreateGroupJoinIterator (this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func, TResult> resultSelector, IEqualityComparer comparer) { ILookup innerKeys = ToLookup (inner, innerKeySelector, comparer); /*Dictionary> innerKeys = new Dictionary> (); foreach (U element in inner) { K innerKey = innerKeySelector (element); if (!innerKeys.ContainsKey (innerKey)) innerKeys.Add (innerKey, new List ()); innerKeys[innerKey].Add (element); }*/ foreach (TOuter element in outer) { TKey outerKey = outerKeySelector (element); if (innerKeys.Contains (outerKey)) yield return resultSelector (element, innerKeys [outerKey]); else yield return resultSelector (element, Empty ()); } } #endregion #region Intersect public static IEnumerable Intersect (this IEnumerable first, IEnumerable second) { return Intersect (first, second, null); } public static IEnumerable Intersect (this IEnumerable first, IEnumerable second, IEqualityComparer comparer) { Check.FirstAndSecond (first, second); if (comparer == null) comparer = EqualityComparer.Default; return CreateIntersectIterator (first, second, comparer); } static IEnumerable CreateIntersectIterator (IEnumerable first, IEnumerable second, IEqualityComparer comparer) { var items = new HashSet (second, comparer); foreach (TSource element in first) { if (items.Remove (element)) yield return element; } } #endregion # region Join public static IEnumerable Join (this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector, IEqualityComparer comparer) { Check.JoinSelectors (outer, inner, outerKeySelector, innerKeySelector, resultSelector); if (comparer == null) comparer = EqualityComparer.Default; return CreateJoinIterator (outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer); } static IEnumerable CreateJoinIterator (this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector, IEqualityComparer comparer) { ILookup innerKeys = ToLookup (inner, innerKeySelector, comparer); /*Dictionary> innerKeys = new Dictionary> (); foreach (U element in inner) { K innerKey = innerKeySelector (element); if (!innerKeys.ContainsKey (innerKey)) innerKeys.Add (innerKey, new List ()); innerKeys[innerKey].Add (element); }*/ foreach (TOuter element in outer) { TKey outerKey = outerKeySelector (element); if (innerKeys.Contains (outerKey)) { foreach (TInner innerElement in innerKeys [outerKey]) yield return resultSelector (element, innerElement); } } } public static IEnumerable Join (this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector) { return outer.Join (inner, outerKeySelector, innerKeySelector, resultSelector, null); } #endregion #region Last static TSource Last (this IEnumerable source, Func predicate, Fallback fallback) { var empty = true; var item = default (TSource); foreach (var element in source) { if (!predicate (element)) continue; item = element; empty = false; } if (!empty) return item; if (fallback == Fallback.Throw) throw new InvalidOperationException (); return item; } public static TSource Last (this IEnumerable source) { Check.Source (source); var collection = source as ICollection; if (collection != null && collection.Count == 0) throw new InvalidOperationException (); var list = source as IList; if (list != null) return list [list.Count - 1]; return source.Last (PredicateOf.Always, Fallback.Throw); } public static TSource Last (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); return source.Last (predicate, Fallback.Throw); } #endregion #region LastOrDefault public static TSource LastOrDefault (this IEnumerable source) { Check.Source (source); var list = source as IList; if (list != null) return list.Count > 0 ? list [list.Count - 1] : default (TSource); return source.Last (PredicateOf.Always, Fallback.Default); } public static TSource LastOrDefault (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); return source.Last (predicate, Fallback.Default); } #endregion #region LongCount public static long LongCount (this IEnumerable source) { Check.Source (source); #if !NET_2_1 var array = source as TSource []; if (array != null) return array.LongLength; #endif long counter = 0; using (var enumerator = source.GetEnumerator ()) while (enumerator.MoveNext ()) counter++; return counter; } public static long LongCount (this IEnumerable source, Func predicate) { Check.SourceAndSelector (source, predicate); long counter = 0; foreach (TSource element in source) if (predicate (element)) counter++; return counter; } #endregion #region Max public static int Max (this IEnumerable source) { Check.Source (source); bool empty = true; var max = int.MinValue; foreach (var element in source){ max = Math.Max (element, max); empty = false; } if (empty) throw new InvalidOperationException (); return max; } public static long Max (this IEnumerable source) { Check.Source (source); bool empty = true; var max = long.MinValue; foreach (var element in source){ max = Math.Max (element, max); empty = false; } if (empty) throw new InvalidOperationException (); return max; } public static double Max (this IEnumerable source) { Check.Source (source); bool empty = true; var max = double.MinValue; foreach (var element in source){ max = Math.Max (element, max); empty = false; } if (empty) throw new InvalidOperationException (); return max; } public static float Max (this IEnumerable source) { Check.Source (source); bool empty = true; var max = float.MinValue; foreach (var element in source){ max = Math.Max (element, max); empty = false; } if (empty) throw new InvalidOperationException (); return max; } public static decimal Max (this IEnumerable source) { Check.Source (source); bool empty = true; var max = decimal.MinValue; foreach (var element in source){ max = Math.Max (element, max); empty = false; } if (empty) throw new InvalidOperationException (); return max; } public static int? Max (this IEnumerable source) { Check.Source (source); bool empty = true; var max = int.MinValue; foreach (var element in source) { if (!element.HasValue) continue; max = Math.Max (element.Value, max); empty = false; } if (empty) return null; return max; } public static long? Max (this IEnumerable source) { Check.Source (source); bool empty = true; var max = long.MinValue; foreach (var element in source) { if (!element.HasValue) continue; max = Math.Max (element.Value, max); empty = false; } if (empty) return null; return max; } public static double? Max (this IEnumerable source) { Check.Source (source); bool empty = true; var max = double.MinValue; foreach (var element in source) { if (!element.HasValue) continue; max = Math.Max (element.Value, max); empty = false; } if (empty) return null; return max; } public static float? Max (this IEnumerable source) { Check.Source (source); bool empty = true; var max = float.MinValue; foreach (var element in source) { if (!element.HasValue) continue; max = Math.Max (element.Value, max); empty = false; } if (empty) return null; return max; } public static decimal? Max (this IEnumerable source) { Check.Source (source); bool empty = true; var max = decimal.MinValue; foreach (var element in source) { if (!element.HasValue) continue; max = Math.Max (element.Value, max); empty = false; } if (empty) return null; return max; } // TODO: test nullable and non-nullable public static TSource Max (this IEnumerable source) { Check.Source (source); var comparer = Comparer.Default; TSource max = default (TSource); if (default (TSource) == null){ foreach (var element in source) { if (element == null) continue; if (max == null || comparer.Compare (element, max) > 0) max = element; } } else { bool empty = true; foreach (var element in source) { if (empty){ max = element; empty = false; continue; } if (comparer.Compare (element, max) > 0) max = element; } if (empty) throw new InvalidOperationException (); } return max; } public static int Max (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; var max = int.MinValue; foreach (var element in source){ max = Math.Max (selector (element), max); empty = false; } if (empty) throw new InvalidOperationException (); return max; } public static long Max (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; var max = long.MinValue; foreach (var element in source){ max = Math.Max (selector (element), max); empty = false; } if (empty) throw new InvalidOperationException (); return max; } public static double Max (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; var max = double.MinValue; foreach (var element in source){ max = Math.Max (selector (element), max); empty = false; } if (empty) throw new InvalidOperationException (); return max; } public static float Max (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; var max = float.MinValue; foreach (var element in source){ max = Math.Max (selector (element), max); empty = false; } if (empty) throw new InvalidOperationException (); return max; } public static decimal Max (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; var max = decimal.MinValue; foreach (var element in source){ max = Math.Max (selector (element), max); empty = false; } if (empty) throw new InvalidOperationException (); return max; } static U Iterate (IEnumerable source, U initValue, Func selector) { bool empty = true; foreach (var element in source) { initValue = selector (element, initValue); empty = false; } if (empty) throw new InvalidOperationException (); return initValue; } public static int? Max (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; int? max = null; foreach (var element in source) { int? item = selector (element); if (!max.HasValue) max = item; else if (item > max) max = item; empty = false; } if (empty) return null; return max; } public static long? Max (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; long? max = null; foreach (var element in source) { long? item = selector (element); if (!max.HasValue) max = item; else if (item > max) max = item; empty = false; } if (empty) return null; return max; } public static double? Max (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; double? max = null; foreach (var element in source) { double? item = selector (element); if (!max.HasValue) max = item; else if (item > max) max = item; empty = false; } if (empty) return null; return max; } public static float? Max (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; float? max = null; foreach (var element in source) { float? item = selector (element); if (!max.HasValue) max = item; else if (item > max) max = item; empty = false; } if (empty) return null; return max; } public static decimal? Max (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; decimal? max = null; foreach (var element in source) { decimal? item = selector (element); if (!max.HasValue) max = item; else if (item > max) max = item; empty = false; } if (empty) return null; return max; } public static TResult Max (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); // TODO: inline return source.Select (selector).Max (); } #endregion #region Min public static int Min (this IEnumerable source) { Check.Source (source); bool empty = true; var min = int.MaxValue; foreach (var element in source){ min = Math.Min (element, min); empty = false; } if (empty) throw new InvalidOperationException (); return min; } public static long Min (this IEnumerable source) { Check.Source (source); bool empty = true; var min = long.MaxValue; foreach (var element in source){ min = Math.Min (element, min); empty = false; } if (empty) throw new InvalidOperationException (); return min; } public static double Min (this IEnumerable source) { Check.Source (source); bool empty = true; var min = double.MaxValue; foreach (var element in source){ min = Math.Min (element, min); empty = false; } if (empty) throw new InvalidOperationException (); return min; } public static float Min (this IEnumerable source) { Check.Source (source); bool empty = true; var min = float.MaxValue; foreach (var element in source){ min = Math.Min (element, min); empty = false; } if (empty) throw new InvalidOperationException (); return min; } public static decimal Min (this IEnumerable source) { Check.Source (source); bool empty = true; var min = decimal.MaxValue; foreach (var element in source){ min = Math.Min (element, min); empty = false; } if (empty) throw new InvalidOperationException (); return min; } public static int? Min (this IEnumerable source) { Check.Source (source); bool empty = true; var min = int.MaxValue; foreach (var element in source) { if (!element.HasValue) continue; min = Math.Min (element.Value, min); empty = false; } if (empty) return null; return min; } public static long? Min (this IEnumerable source) { Check.Source (source); bool empty = true; var min = long.MaxValue; foreach (var element in source) { if (!element.HasValue) continue; min = Math.Min (element.Value, min); empty = false; } if (empty) return null; return min; } public static double? Min (this IEnumerable source) { Check.Source (source); bool empty = true; var min = double.MaxValue; foreach (var element in source) { if (!element.HasValue) continue; min = Math.Min (element.Value, min); empty = false; } if (empty) return null; return min; } public static float? Min (this IEnumerable source) { Check.Source (source); bool empty = true; var min = float.MaxValue; foreach (var element in source) { if (!element.HasValue) continue; min = Math.Min (element.Value, min); empty = false; } if (empty) return null; return min; } public static decimal? Min (this IEnumerable source) { Check.Source (source); bool empty = true; var min = decimal.MaxValue; foreach (var element in source) { if (!element.HasValue) continue; min = Math.Min (element.Value, min); empty = false; } if (empty) return null; return min; } public static TSource Min (this IEnumerable source) { Check.Source (source); var comparer = Comparer.Default; TSource min = default (TSource); if (default (TSource) == null){ foreach (var element in source) { if (element == null) continue; if (min == null || comparer.Compare (element, min) < 0) min = element; } } else { bool empty = true; foreach (var element in source) { if (empty){ min = element; empty = false; continue; } if (comparer.Compare (element, min) < 0) min = element; } if (empty) throw new InvalidOperationException (); } return min; } public static int Min (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; var min = int.MaxValue; foreach (var element in source){ min = Math.Min (selector (element), min); empty = false; } if (empty) throw new InvalidOperationException (); return min; } public static long Min (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; var min = long.MaxValue; foreach (var element in source){ min = Math.Min (selector (element), min); empty = false; } if (empty) throw new InvalidOperationException (); return min; } public static double Min (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; var min = double.MaxValue; foreach (var element in source){ min = Math.Min (selector (element), min); empty = false; } if (empty) throw new InvalidOperationException (); return min; } public static float Min (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; var min = float.MaxValue; foreach (var element in source){ min = Math.Min (selector (element), min); empty = false; } if (empty) throw new InvalidOperationException (); return min; } public static decimal Min (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; var min = decimal.MaxValue; foreach (var element in source){ min = Math.Min (selector (element), min); empty = false; } if (empty) throw new InvalidOperationException (); return min; } public static int? Min (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; int? min = null; foreach (var element in source) { int? item = selector (element); if (!min.HasValue) min = item; else if (item < min) min = item; empty = false; } if (empty) return null; return min; } public static long? Min (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; long? min = null; foreach (var element in source) { long? item = selector (element); if (!min.HasValue) min = item; else if (item < min) min = item; empty = false; } if (empty) return null; return min; } public static float? Min (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; float? min = null; foreach (var element in source) { float? item = selector (element); if (!min.HasValue) min = item; else if (item < min) min = item; empty = false; } if (empty) return null; return min; } public static double? Min (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; double? min = null; foreach (var element in source) { double? item = selector (element); if (!min.HasValue) min = item; else if (item < min) min = item; empty = false; } if (empty) return null; return min; } public static decimal? Min (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool empty = true; decimal? min = null; foreach (var element in source) { decimal? item = selector (element); if (!min.HasValue) min = item; else if (item < min) min = item; empty = false; } if (empty) return null; return min; } public static TResult Min (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); // TODO: inline return source.Select (selector).Min (); } #endregion #region OfType public static IEnumerable OfType (this IEnumerable source) { Check.Source (source); return CreateOfTypeIterator (source); } static IEnumerable CreateOfTypeIterator (IEnumerable source) { foreach (object element in source) if (element is TResult) yield return (TResult) element; } #endregion #region OrderBy public static IOrderedEnumerable OrderBy (this IEnumerable source, Func keySelector) { return OrderBy (source, keySelector, null); } public static IOrderedEnumerable OrderBy (this IEnumerable source, Func keySelector, IComparer comparer) { Check.SourceAndKeySelector (source, keySelector); return new OrderedSequence (source, keySelector, comparer, SortDirection.Ascending); } #endregion #region OrderByDescending public static IOrderedEnumerable OrderByDescending (this IEnumerable source, Func keySelector) { return OrderByDescending (source, keySelector, null); } public static IOrderedEnumerable OrderByDescending (this IEnumerable source, Func keySelector, IComparer comparer) { Check.SourceAndKeySelector (source, keySelector); return new OrderedSequence (source, keySelector, comparer, SortDirection.Descending); } #endregion #region Range public static IEnumerable Range (int start, int count) { if (count < 0) throw new ArgumentOutOfRangeException ("count"); if (((long) start + count) - 1L > int.MaxValue) throw new ArgumentOutOfRangeException (); return CreateRangeIterator (start, count); } static IEnumerable CreateRangeIterator (int start, int count) { for (int i = 0; i < count; i++) yield return start + i; } #endregion #region Repeat public static IEnumerable Repeat (TResult element, int count) { if (count < 0) throw new ArgumentOutOfRangeException (); return CreateRepeatIterator (element, count); } static IEnumerable CreateRepeatIterator (TResult element, int count) { for (int i = 0; i < count; i++) yield return element; } #endregion #region Reverse public static IEnumerable Reverse (this IEnumerable source) { Check.Source (source); return CreateReverseIterator (source); } static IEnumerable CreateReverseIterator (IEnumerable source) { var list = source as IList; if (list == null) list = new List (source); for (int i = list.Count - 1; i >= 0; i--) yield return list [i]; } #endregion #region Select public static IEnumerable Select (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); return CreateSelectIterator (source, selector); } static IEnumerable CreateSelectIterator (IEnumerable source, Func selector) { foreach (var element in source) yield return selector (element); } public static IEnumerable Select (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); return CreateSelectIterator (source, selector); } static IEnumerable CreateSelectIterator (IEnumerable source, Func selector) { int counter = 0; foreach (TSource element in source) { yield return selector (element, counter); counter++; } } #endregion #region SelectMany public static IEnumerable SelectMany (this IEnumerable source, Func> selector) { Check.SourceAndSelector (source, selector); return CreateSelectManyIterator (source, selector); } static IEnumerable CreateSelectManyIterator (IEnumerable source, Func> selector) { foreach (TSource element in source) foreach (TResult item in selector (element)) yield return item; } public static IEnumerable SelectMany (this IEnumerable source, Func> selector) { Check.SourceAndSelector (source, selector); return CreateSelectManyIterator (source, selector); } static IEnumerable CreateSelectManyIterator (IEnumerable source, Func> selector) { int counter = 0; foreach (TSource element in source) { foreach (TResult item in selector (element, counter)) yield return item; counter++; } } public static IEnumerable SelectMany (this IEnumerable source, Func> collectionSelector, Func resultSelector) { Check.SourceAndCollectionSelectors (source, collectionSelector, resultSelector); return CreateSelectManyIterator (source, collectionSelector, resultSelector); } static IEnumerable CreateSelectManyIterator (IEnumerable source, Func> collectionSelector, Func selector) { foreach (TSource element in source) foreach (TCollection collection in collectionSelector (element)) yield return selector (element, collection); } public static IEnumerable SelectMany (this IEnumerable source, Func> collectionSelector, Func resultSelector) { Check.SourceAndCollectionSelectors (source, collectionSelector, resultSelector); return CreateSelectManyIterator (source, collectionSelector, resultSelector); } static IEnumerable CreateSelectManyIterator (IEnumerable source, Func> collectionSelector, Func selector) { int counter = 0; foreach (TSource element in source) foreach (TCollection collection in collectionSelector (element, counter++)) yield return selector (element, collection); } #endregion #region Single static TSource Single (this IEnumerable source, Func predicate, Fallback fallback) { var found = false; var item = default (TSource); foreach (var element in source) { if (!predicate (element)) continue; if (found) throw new InvalidOperationException (); found = true; item = element; } if (!found && fallback == Fallback.Throw) throw new InvalidOperationException (); return item; } public static TSource Single (this IEnumerable source) { Check.Source (source); return source.Single (PredicateOf.Always, Fallback.Throw); } public static TSource Single (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); return source.Single (predicate, Fallback.Throw); } #endregion #region SingleOrDefault public static TSource SingleOrDefault (this IEnumerable source) { Check.Source (source); return source.Single (PredicateOf.Always, Fallback.Default); } public static TSource SingleOrDefault (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); return source.Single (predicate, Fallback.Default); } #endregion #region Skip public static IEnumerable Skip (this IEnumerable source, int count) { Check.Source (source); return CreateSkipIterator (source, count); } static IEnumerable CreateSkipIterator (IEnumerable source, int count) { var enumerator = source.GetEnumerator (); try { while (count-- > 0) if (!enumerator.MoveNext ()) yield break; while (enumerator.MoveNext ()) yield return enumerator.Current; } finally { enumerator.Dispose (); } } #endregion #region SkipWhile public static IEnumerable SkipWhile (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); return CreateSkipWhileIterator (source, predicate); } static IEnumerable CreateSkipWhileIterator (IEnumerable source, Func predicate) { bool yield = false; foreach (TSource element in source) { if (yield) yield return element; else if (!predicate (element)) { yield return element; yield = true; } } } public static IEnumerable SkipWhile (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); return CreateSkipWhileIterator (source, predicate); } static IEnumerable CreateSkipWhileIterator (IEnumerable source, Func predicate) { int counter = 0; bool yield = false; foreach (TSource element in source) { if (yield) yield return element; else if (!predicate (element, counter)) { yield return element; yield = true; } counter++; } } #endregion #region Sum public static int Sum (this IEnumerable source) { Check.Source (source); int total = 0; foreach (var element in source) total = checked (total + element); return total; } public static int? Sum (this IEnumerable source) { Check.Source (source); int total = 0; foreach (var element in source) { if (element.HasValue) total = checked (total + element.Value); } return total; } public static int Sum (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); int total = 0; foreach (var element in source) total = checked (total + selector (element)); return total; } public static int? Sum (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); int total = 0; foreach (var element in source) { var value = selector (element); if (value.HasValue) total = checked (total + value.Value); } return total; } public static long Sum (this IEnumerable source) { Check.Source (source); long total = 0; foreach (var element in source) total = checked (total + element); return total; } public static long? Sum (this IEnumerable source) { Check.Source (source); long total = 0; foreach (var element in source) { if (element.HasValue) total = checked (total + element.Value); } return total; } public static long Sum (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); long total = 0; foreach (var element in source) total = checked (total + selector (element)); return total; } public static long? Sum (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); long total = 0; foreach (var element in source) { var value = selector (element); if (value.HasValue) total = checked (total + value.Value); } return total; } public static double Sum (this IEnumerable source) { Check.Source (source); double total = 0; foreach (var element in source) total = checked (total + element); return total; } public static double? Sum (this IEnumerable source) { Check.Source (source); double total = 0; foreach (var element in source) { if (element.HasValue) total = checked (total + element.Value); } return total; } public static double Sum (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); double total = 0; foreach (var element in source) total = checked (total + selector (element)); return total; } public static double? Sum (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); double total = 0; foreach (var element in source) { var value = selector (element); if (value.HasValue) total = checked (total + value.Value); } return total; } public static float Sum (this IEnumerable source) { Check.Source (source); float total = 0; foreach (var element in source) total = checked (total + element); return total; } public static float? Sum (this IEnumerable source) { Check.Source (source); float total = 0; foreach (var element in source) { if (element.HasValue) total = checked (total + element.Value); } return total; } public static float Sum (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); float total = 0; foreach (var element in source) total = checked (total + selector (element)); return total; } public static float? Sum (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); float total = 0; foreach (var element in source) { var value = selector (element); if (value.HasValue) total = checked (total + value.Value); } return total; } public static decimal Sum (this IEnumerable source) { Check.Source (source); decimal total = 0; foreach (var element in source) total = checked (total + element); return total; } public static decimal? Sum (this IEnumerable source) { Check.Source (source); decimal total = 0; foreach (var element in source) { if (element.HasValue) total = checked (total + element.Value); } return total; } public static decimal Sum (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); decimal total = 0; foreach (var element in source) total = checked (total + selector (element)); return total; } public static decimal? Sum (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); decimal total = 0; foreach (var element in source) { var value = selector (element); if (value.HasValue) total = checked (total + value.Value); } return total; } #endregion #region Take public static IEnumerable Take (this IEnumerable source, int count) { Check.Source (source); return CreateTakeIterator (source, count); } static IEnumerable CreateTakeIterator (IEnumerable source, int count) { if (count <= 0) yield break; int counter = 0; foreach (TSource element in source) { yield return element; if (++counter == count) yield break; } } #endregion #region TakeWhile public static IEnumerable TakeWhile (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); return CreateTakeWhileIterator (source, predicate); } static IEnumerable CreateTakeWhileIterator (IEnumerable source, Func predicate) { foreach (var element in source) { if (!predicate (element)) yield break; yield return element; } } public static IEnumerable TakeWhile (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); return CreateTakeWhileIterator (source, predicate); } static IEnumerable CreateTakeWhileIterator (IEnumerable source, Func predicate) { int counter = 0; foreach (var element in source) { if (!predicate (element, counter)) yield break; yield return element; counter++; } } #endregion #region ThenBy public static IOrderedEnumerable ThenBy (this IOrderedEnumerable source, Func keySelector) { return ThenBy (source, keySelector, null); } public static IOrderedEnumerable ThenBy (this IOrderedEnumerable source, Func keySelector, IComparer comparer) { Check.SourceAndKeySelector (source, keySelector); return source.CreateOrderedEnumerable (keySelector, comparer, false); } #endregion #region ThenByDescending public static IOrderedEnumerable ThenByDescending (this IOrderedEnumerable source, Func keySelector) { return ThenByDescending (source, keySelector, null); } public static IOrderedEnumerable ThenByDescending (this IOrderedEnumerable source, Func keySelector, IComparer comparer) { Check.SourceAndKeySelector (source, keySelector); return source.CreateOrderedEnumerable (keySelector, comparer, true); } #endregion #region ToArray public static TSource [] ToArray (this IEnumerable source) { Check.Source (source); var collection = source as ICollection; if (collection != null) { var array = new TSource [collection.Count]; collection.CopyTo (array, 0); return array; } return new List (source).ToArray (); } #endregion #region ToDictionary public static Dictionary ToDictionary (this IEnumerable source, Func keySelector, Func elementSelector) { return ToDictionary (source, keySelector, elementSelector, null); } public static Dictionary ToDictionary (this IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer) { Check.SourceAndKeyElementSelectors (source, keySelector, elementSelector); if (comparer == null) comparer = EqualityComparer.Default; var dict = new Dictionary (comparer); foreach (var e in source) dict.Add (keySelector (e), elementSelector (e)); return dict; } public static Dictionary ToDictionary (this IEnumerable source, Func keySelector) { return ToDictionary (source, keySelector, null); } public static Dictionary ToDictionary (this IEnumerable source, Func keySelector, IEqualityComparer comparer) { return ToDictionary (source, keySelector, Function.Identity, comparer); } #endregion #region ToList public static List ToList (this IEnumerable source) { Check.Source (source); return new List (source); } #endregion #region ToLookup public static ILookup ToLookup (this IEnumerable source, Func keySelector) { return ToLookup (source, keySelector, Function.Identity, null); } public static ILookup ToLookup (this IEnumerable source, Func keySelector, IEqualityComparer comparer) { return ToLookup (source, keySelector, element => element, comparer); } public static ILookup ToLookup (this IEnumerable source, Func keySelector, Func elementSelector) { return ToLookup (source, keySelector, elementSelector, null); } public static ILookup ToLookup (this IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer) { Check.SourceAndKeyElementSelectors (source, keySelector, elementSelector); List nullKeyElements = null; var dictionary = new Dictionary> (comparer ?? EqualityComparer.Default); foreach (var element in source) { var key = keySelector (element); List list; if (key == null) { if (nullKeyElements == null) nullKeyElements = new List (); list = nullKeyElements; } else if (!dictionary.TryGetValue (key, out list)) { list = new List (); dictionary.Add (key, list); } list.Add (elementSelector (element)); } return new Lookup (dictionary, nullKeyElements); } #endregion #region SequenceEqual public static bool SequenceEqual (this IEnumerable first, IEnumerable second) { return first.SequenceEqual (second, null); } public static bool SequenceEqual (this IEnumerable first, IEnumerable second, IEqualityComparer comparer) { Check.FirstAndSecond (first, second); if (comparer == null) comparer = EqualityComparer.Default; using (IEnumerator first_enumerator = first.GetEnumerator (), second_enumerator = second.GetEnumerator ()) { while (first_enumerator.MoveNext ()) { if (!second_enumerator.MoveNext ()) return false; if (!comparer.Equals (first_enumerator.Current, second_enumerator.Current)) return false; } return !second_enumerator.MoveNext (); } } #endregion #region Union public static IEnumerable Union (this IEnumerable first, IEnumerable second) { Check.FirstAndSecond (first, second); return first.Union (second, null); } public static IEnumerable Union (this IEnumerable first, IEnumerable second, IEqualityComparer comparer) { Check.FirstAndSecond (first, second); if (comparer == null) comparer = EqualityComparer.Default; return CreateUnionIterator (first, second, comparer); } static IEnumerable CreateUnionIterator (IEnumerable first, IEnumerable second, IEqualityComparer comparer) { var items = new HashSet (comparer); foreach (var element in first) { if (! items.Contains (element)) { items.Add (element); yield return element; } } foreach (var element in second) { if (! items.Contains (element)) { items.Add (element); yield return element; } } } #endregion #if NET_4_0 || MOONLIGHT #region Zip public static IEnumerable Zip (this IEnumerable first, IEnumerable second, Func resultSelector) { Check.FirstAndSecond (first, second); if (resultSelector == null) throw new ArgumentNullException ("resultSelector"); return CreateZipIterator (first, second, resultSelector); } static IEnumerable CreateZipIterator (IEnumerable first, IEnumerable second, Func selector) { using (IEnumerator first_enumerator = first.GetEnumerator ()) { using (IEnumerator second_enumerator = second.GetEnumerator ()) { while (first_enumerator.MoveNext () && second_enumerator.MoveNext ()) { yield return selector (first_enumerator.Current, second_enumerator.Current); } } } } #endregion #endif #region Where public static IEnumerable Where (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); return CreateWhereIterator (source, predicate); } static IEnumerable CreateWhereIterator (IEnumerable source, Func predicate) { foreach (TSource element in source) if (predicate (element)) yield return element; } public static IEnumerable Where (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); return CreateWhereIterator (source, predicate); } static IEnumerable CreateWhereIterator (this IEnumerable source, Func predicate) { int counter = 0; foreach (TSource element in source) { if (predicate (element, counter)) yield return element; counter++; } } #endregion internal static ReadOnlyCollection ToReadOnlyCollection (this IEnumerable source) { if (source == null) return ReadOnlyCollectionOf.Empty; var ro = source as ReadOnlyCollection; if (ro != null) return ro; return new ReadOnlyCollection (source.ToArray ()); } } }