// // 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 { #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"); TAccumulate result = seed; foreach (TSource 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 (TSource element in source) if (!predicate (element)) return false; return true; } #endregion #region Any public static bool Any (this IEnumerable source) { Check.Source (source); 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) { return Average (source, (a, b) => a + b, (a, b) => a / b); } public static double Average (this IEnumerable source) { return Average (source, (a, b) => a + b, (a, b) => a / b); } public static double Average (this IEnumerable source) { return Average (source, (a, b) => a + b, (a, b) => a / b); } public static float Average (this IEnumerable source) { return Average (source, (a, b) => a + b, (a, b) => a / b); } public static decimal Average (this IEnumerable source) { return Average (source, (a, b) => a + b, (a, b) => a / b); } static TR Average (this IEnumerable source, Func func, Func result) { Check.Source (source); TA total = default (TA); int counter = 0; foreach (var element in source) { total = func (total, element); ++counter; } if (counter == 0) throw new InvalidOperationException (); return result (total, counter); } public static double? Average (this IEnumerable source) { Check.Source (source); bool onlyNull = true; long sum = 0; long counter = 0; foreach (int? element in source) { if (element.HasValue) { onlyNull = false; sum += element.Value; counter++; } } return (onlyNull ? null : (double?) sum / (double?) counter); } public static double? Average (this IEnumerable source) { Check.Source (source); bool onlyNull = true; long sum = 0; long counter = 0; foreach (long? element in source) { if (element.HasValue) { onlyNull = false; sum += element.Value; counter++; } } return (onlyNull ? null : (double?) sum / (double?) counter); } public static double? Average (this IEnumerable source) { Check.Source (source); bool onlyNull = true; double sum = 0; double counter = 0; foreach (double? element in source) { if (element.HasValue) { onlyNull = false; sum += element.Value; counter++; } } return (onlyNull ? null : (double?) (sum / counter)); } public static decimal? Average (this IEnumerable source) { Check.Source (source); bool onlyNull = true; decimal sum = 0; decimal counter = 0; foreach (decimal? element in source) { if (element.HasValue) { onlyNull = false; sum += element.Value; counter++; } } return (onlyNull ? null : (decimal?) (sum / counter)); } public static float? Average (this IEnumerable source) { Check.Source (source); float sum = 0; float counter = 0; foreach (float? element in source) { if (element.HasValue) { sum += element.Value; ++counter; } } if (counter == 0) return null; return sum / counter; } public static double Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); long sum = 0; long counter = 0; foreach (TSource item in source) { sum += selector (item); counter++; } if (counter == 0) throw new InvalidOperationException (); else return (double) sum / (double) counter; } public static double? Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool onlyNull = true; long sum = 0; long counter = 0; foreach (TSource item in source) { int? element = selector (item); if (element.HasValue) { onlyNull = false; sum += element.Value; counter++; } } return (onlyNull ? null : (double?) sum / (double?) counter); } public static double Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); long sum = 0; long counter = 0; foreach (TSource item in source) { sum += selector (item); counter++; } if (counter == 0) throw new InvalidOperationException (); else return (double) sum / (double) counter; } public static double? Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool onlyNull = true; long sum = 0; long counter = 0; foreach (TSource item in source) { long? element = selector (item); if (element.HasValue) { onlyNull = false; sum += element.Value; counter++; } } return (onlyNull ? null : (double?) sum / (double?) counter); } public static double Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); double sum = 0; double counter = 0; foreach (TSource item in source) { sum += selector (item); counter++; } if (counter == 0) throw new InvalidOperationException (); else return sum / counter; } public static double? Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool onlyNull = true; double sum = 0; double counter = 0; foreach (TSource item in source) { double? element = selector (item); if (element.HasValue) { onlyNull = false; sum += element.Value; counter++; } } return (onlyNull ? null : (double?) (sum / counter)); } public static float Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); float sum = 0; float counter = 0; foreach (TSource item in source) { sum += selector (item); ++counter; } if (counter == 0) throw new InvalidOperationException (); return sum / counter; } public static float? Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); float sum = 0; float counter = 0; foreach (TSource item in source) { float? value = selector (item); if (value.HasValue) { sum += value.Value; ++counter; } } if (counter == 0) throw new InvalidOperationException (); return sum / counter; } public static decimal Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); decimal sum = 0; decimal counter = 0; foreach (TSource item in source) { sum += selector (item); counter++; } if (counter == 0) throw new InvalidOperationException (); else return sum / counter; } public static decimal? Average (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool onlyNull = true; decimal sum = 0; decimal counter = 0; foreach (TSource item in source) { decimal? element = selector (item); if (element.HasValue) { onlyNull = false; sum += element.Value; counter++; } } return (onlyNull ? null : (decimal?) (sum / counter)); } #endregion #region Cast public static IEnumerable Cast (this IEnumerable source) { Check.Source (source); return CreateCastIterator (source); } static IEnumerable CreateCastIterator (IEnumerable source) { foreach (object element in source) yield return (TResult) 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 (TSource e in source) { if (comparer.Equals (e, 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 ()) counter++; return counter; } public static int Count (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); int counter = 0; foreach (var element in source) if (selector (element)) 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 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]; int counter = 0; foreach (var element in source) { if (counter == index) return element; counter++; } throw new ArgumentOutOfRangeException (); } #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); int counter = 0; foreach (TSource element in source) { if (counter == index) return element; counter++; } return default (TSource); } #endregion #region Empty public static IEnumerable Empty () { return new TResult [0]; } #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 (Distinct (second)); foreach (TSource element in first) { if (! items.Contains (element, comparer)) yield return element; } } #endregion #region First public static TSource First (this IEnumerable source) { Check.Source (source); foreach (TSource element in source) return element; throw new InvalidOperationException (); } public static TSource First (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); foreach (TSource element in source) { if (predicate (element)) return element; } throw new InvalidOperationException (); } #endregion #region FirstOrDefault public static TSource FirstOrDefault (this IEnumerable source) { Check.Source (source); foreach (TSource element in source) return element; return default (TSource); } public static TSource FirstOrDefault (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); foreach (TSource element in source) { if (predicate (element)) return element; } return default (TSource); } #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); Dictionary> groups = new Dictionary> (); List 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 (KeyValuePair> group in groups) { if (counter == nullCounter) { Grouping nullGroup = new Grouping (default (TKey), nullList); yield return nullGroup; counter++; } Grouping grouping = new Grouping (group.Key, group.Value); yield return grouping; 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); Dictionary> groups = new Dictionary> (); List 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 (KeyValuePair> group in groups) { if (counter == nullCounter) { Grouping nullGroup = new Grouping (default (TKey), nullList); yield return nullGroup; counter++; } Grouping grouping = new Grouping (group.Key, group.Value); yield return grouping; counter++; } } public static IEnumerable GroupBy (this IEnumerable source, Func keySelector, Func elementSelector, Func, TResult> resultSelector) { return GroupBy (source, keySelector, elementSelector, resultSelector, null); } [MonoTODO] public static IEnumerable GroupBy (this IEnumerable source, Func keySelector, Func elementSelector, Func, TResult> resultSelector, IEqualityComparer comparer) { throw new NotImplementedException (); } public static IEnumerable GroupBy (this IEnumerable source, Func keySelector, Func, TResult> resultSelector) { return GroupBy (source, keySelector, resultSelector, null); } [MonoTODO] public static IEnumerable GroupBy (this IEnumerable source, Func keySelector, Func, TResult> resultSelector, IEqualityComparer comparer) { throw new NotImplementedException (); } #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; 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.Contains (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; 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 Join (outer, inner, outerKeySelector, innerKeySelector, resultSelector, null); } # endregion #region Last public static TSource Last (this IEnumerable source) { Check.Source (source); bool noElements = true; TSource lastElement = default (TSource); foreach (TSource element in source) { if (noElements) noElements = false; lastElement = element; } if (!noElements) return lastElement; else throw new InvalidOperationException (); } public static TSource Last (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); bool noElements = true; TSource lastElement = default (TSource); foreach (TSource element in source) { if (predicate (element)) { if (noElements) noElements = false; lastElement = element; } } if (!noElements) return lastElement; else throw new InvalidOperationException (); } #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); TSource lastElement = default (TSource); foreach (TSource element in source) lastElement = element; return lastElement; } public static TSource LastOrDefault (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); TSource lastElement = default (TSource); foreach (TSource element in source) { if (predicate (element)) lastElement = element; } return lastElement; } #endregion #region LongCount public static long LongCount (this IEnumerable source) { Check.Source (source); long counter = 0; using (var enumerator = source.GetEnumerator ()) while (enumerator.MoveNext ()) counter++; return counter; } public static long LongCount (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); long counter = 0; foreach (TSource element in source) if (selector (element)) counter++; return counter; } #endregion #region Max public static int Max (this IEnumerable source) { return Iterate (source, int.MinValue, (a, b) => a > b); } public static long Max (this IEnumerable source) { return Iterate (source, long.MinValue, (a, b) => a > b); } public static double Max (this IEnumerable source) { return Iterate (source, double.MinValue, (a, b) => a > b); } public static float Max (this IEnumerable source) { return Iterate (source, float.MinValue, (a, b) => a > b); } public static decimal Max (this IEnumerable source) { return Iterate (source, decimal.MinValue, (a, b) => a > b); } static T Iterate (IEnumerable source, T initValue, Func selector) { Check.SourceAndSelector (source, selector); int counter = 0; foreach (var element in source) { if (selector (element, initValue)) initValue = element; ++counter; } if (counter == 0) throw new InvalidOperationException (); return initValue; } public static int? Max (this IEnumerable source) { return IterateNullable (source, int.MinValue, (a, b) => a > b); } public static long? Max (this IEnumerable source) { return IterateNullable (source, long.MinValue, (a, b) => a > b); } public static double? Max (this IEnumerable source) { return IterateNullable (source, double.MinValue, (a, b) => a > b); } public static float? Max (this IEnumerable source) { return IterateNullable (source, float.MinValue, (a, b) => a > b); } public static decimal? Max (this IEnumerable source) { return IterateNullable (source, decimal.MinValue, (a, b) => a > b); } static T? IterateNullable (IEnumerable source, T initValue, Func selector) where T : struct { Check.SourceAndSelector (source, selector); int counter = 0; T? value = initValue; foreach (var element in source) { if (!element.HasValue) continue; if (selector (element.Value, value)) value = element; ++counter; } if (counter == 0) return null; return value; } public static TSource Max (this IEnumerable source) { Check.Source (source); bool notAssigned = true; TSource maximum = default (TSource); int counter = 0; foreach (TSource element in source) { if (notAssigned) { maximum = element; notAssigned = false; } else { int comparison; if (element is IComparable) comparison = ((IComparable) element).CompareTo (maximum); else if (element is System.IComparable) comparison = ((System.IComparable) element).CompareTo (maximum); else throw new ArgumentNullException (); if (comparison > 0) maximum = element; } counter++; } if (counter == 0) throw new InvalidOperationException (); else return maximum; } public static int Max (this IEnumerable source, Func selector) { return Iterate (source, int.MinValue, (a, b) => { var v = selector (a); return v > b ? v : b; }); } public static long Max (this IEnumerable source, Func selector) { return Iterate (source, long.MinValue, (a, b) => { var v = selector (a); return v > b ? v : b; }); } public static double Max (this IEnumerable source, Func selector) { return Iterate (source, double.MinValue, (a, b) => { var v = selector (a); return v > b ? v : b; }); } public static float Max (this IEnumerable source, Func selector) { return Iterate (source, float.MinValue, (a, b) => { var v = selector (a); return v > b ? v : b; }); } public static decimal Max (this IEnumerable source, Func selector) { return Iterate (source, decimal.MinValue, (a, b) => { var v = selector (a); return v > b ? v : b; }); } static U Iterate (IEnumerable source, U initValue, Func selector) { Check.SourceAndSelector (source, selector); int counter = 0; foreach (var element in source) { initValue = selector (element, initValue); ++counter; } if (counter == 0) throw new InvalidOperationException (); return initValue; } static U? IterateNullable (IEnumerable source, U initialValue, Func selector) where U : struct { Check.SourceAndSelector (source, selector); int counter = 0; U? value = initialValue; foreach (var element in source) { value = selector (element, value); if (!value.HasValue) continue; ++counter; } if (counter == 0) return null; return value; } public static int? Max (this IEnumerable source, Func selector) { return IterateNullable (source, int.MinValue, (a, b) => { var v = selector (a); return v > b ? v : b; }); } public static long? Max (this IEnumerable source, Func selector) { return IterateNullable (source, long.MinValue, (a, b) => { var v = selector (a); return v > b ? v : b; }); } public static double? Max (this IEnumerable source, Func selector) { return IterateNullable (source, double.MinValue, (a, b) => { var v = selector (a); return v > b ? v : b; }); } public static float? Max (this IEnumerable source, Func selector) { return IterateNullable (source, float.MinValue, (a, b) => { var v = selector (a); return v > b ? v : b; }); } public static decimal? Max (this IEnumerable source, Func selector) { return IterateNullable (source, decimal.MinValue, (a, b) => { var v = selector (a); return v > b ? v : b; }); } public static TResult Max (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool notAssigned = true; TResult maximum = default (TResult); int counter = 0; foreach (TSource item in source) { TResult element = selector (item); if (notAssigned) { maximum = element; notAssigned = false; } else { int comparison; if (element is IComparable) comparison = ((IComparable) element).CompareTo (maximum); else if (element is System.IComparable) comparison = ((System.IComparable) element).CompareTo (maximum); else throw new ArgumentNullException (); if (comparison > 0) maximum = element; } counter++; } if (counter == 0) throw new InvalidOperationException (); else return maximum; } #endregion #region Min public static int Min (this IEnumerable source) { return Iterate (source, int.MaxValue, (a, b) => a < b); } public static long Min (this IEnumerable source) { return Iterate (source, long.MaxValue, (a, b) => a < b); } public static double Min (this IEnumerable source) { return Iterate (source, double.MaxValue, (a, b) => a < b); } public static float Min (this IEnumerable source) { return Iterate (source, float.MaxValue, (a, b) => a < b); } public static decimal Min (this IEnumerable source) { return Iterate (source, decimal.MaxValue, (a, b) => a < b); } public static int? Min (this IEnumerable source) { return IterateNullable (source, int.MaxValue, (a, b) => a < b); } public static long? Min (this IEnumerable source) { return IterateNullable (source, long.MaxValue, (a, b) => a < b); } public static double? Min (this IEnumerable source) { return IterateNullable (source, double.MaxValue, (a, b) => a < b); } public static float? Min (this IEnumerable source) { return IterateNullable (source, float.MaxValue, (a, b) => a < b); } public static decimal? Min (this IEnumerable source) { return IterateNullable (source, decimal.MaxValue, (a, b) => a < b); } public static TSource Min (this IEnumerable source) { Check.Source (source); bool notAssigned = true; TSource minimum = default (TSource); int counter = 0; foreach (TSource element in source) { if (notAssigned) { minimum = element; notAssigned = false; } else { int comparison; if (element is IComparable) comparison = ((IComparable) element).CompareTo (minimum); else if (element is System.IComparable) comparison = ((System.IComparable) element).CompareTo (minimum); else throw new ArgumentNullException (); if (comparison < 0) minimum = element; } counter++; } if (counter == 0) throw new InvalidOperationException (); else return minimum; } public static int Min (this IEnumerable source, Func selector) { return Iterate (source, int.MaxValue, (a, b) => { var v = selector (a); return v < b ? v : b; }); } public static long Min (this IEnumerable source, Func selector) { return Iterate (source, long.MaxValue, (a, b) => { var v = selector (a); return v < b ? v : b; }); } public static double Min (this IEnumerable source, Func selector) { return Iterate (source, double.MaxValue, (a, b) => { var v = selector (a); return v < b ? v : b; }); } public static float Min (this IEnumerable source, Func selector) { return Iterate (source, float.MaxValue, (a, b) => { var v = selector (a); return v < b ? v : b; }); } public static decimal Min (this IEnumerable source, Func selector) { return Iterate (source, decimal.MaxValue, (a, b) => { var v = selector (a); return v < b ? v : b; }); } public static int? Min (this IEnumerable source, Func selector) { return IterateNullable (source, int.MaxValue, (a, b) => { var v = selector (a); return v < b ? v : b; }); } public static long? Min (this IEnumerable source, Func selector) { return IterateNullable (source, long.MaxValue, (a, b) => { var v = selector (a); return v < b ? v : b; }); } public static double? Min (this IEnumerable source, Func selector) { return IterateNullable (source, float.MaxValue, (a, b) => { var v = selector (a); return v < b ? v : b; }); } public static double? Min (this IEnumerable source, Func selector) { return IterateNullable (source, double.MaxValue, (a, b) => { var v = selector (a); return v < b ? v : b; }); } public static decimal? Min (this IEnumerable source, Func selector) { return IterateNullable (source, decimal.MaxValue, (a, b) => { var v = selector (a); return v < b ? v : b; }); } public static TResult Min (this IEnumerable source, Func selector) { Check.SourceAndSelector (source, selector); bool notAssigned = true; TResult minimum = default (TResult); int counter = 0; foreach (TSource item in source) { TResult element = selector (item); if (notAssigned) { minimum = element; notAssigned = false; } else { int comparison; if (element is IComparable) comparison = ((IComparable) element).CompareTo (minimum); else if (element is System.IComparable) comparison = ((System.IComparable) element).CompareTo (minimum); else throw new ArgumentNullException (); if (comparison < 0) minimum = element; } counter++; } if (counter == 0) throw new InvalidOperationException (); else return minimum; } #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 InternalOrderedSequence ( source, keySelector, comparer, false); } #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 InternalOrderedSequence ( source, keySelector, comparer, true); } #endregion #region Range public static IEnumerable Range (int start, int count) { if (count < 0) throw new ArgumentOutOfRangeException ("count"); long upto = ((long) start + count) - 1; if (upto > int.MaxValue) throw new ArgumentOutOfRangeException (); return CreateRangeIterator (start, (int) upto); } static IEnumerable CreateRangeIterator (int start, int upto) { for (int i = start; i <= upto; i++) yield return 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 = new List (source); list.Reverse (); return list; } #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 (TSource 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 selector) { Check.SourceAndCollectionSelectors (source, collectionSelector, selector); return CreateSelectManyIterator (source, collectionSelector, selector); } 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 selector) { Check.SourceAndCollectionSelectors (source, collectionSelector, selector); return CreateSelectManyIterator (source, collectionSelector, selector); } 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 public static TSource Single (this IEnumerable source) { Check.Source (source); bool otherElement = false; TSource singleElement = default (TSource); foreach (TSource element in source) { if (otherElement) throw new InvalidOperationException (); if (!otherElement) otherElement = true; singleElement = element; } if (otherElement) return singleElement; else throw new InvalidOperationException (); } public static TSource Single (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); bool otherElement = false; TSource singleElement = default (TSource); foreach (TSource element in source) { if (predicate (element)) { if (otherElement) throw new InvalidOperationException (); if (!otherElement) otherElement = true; singleElement = element; } } if (otherElement) return singleElement; else throw new InvalidOperationException (); } #endregion #region SingleOrDefault public static TSource SingleOrDefault (this IEnumerable source) { Check.Source (source); bool otherElement = false; TSource singleElement = default (TSource); foreach (TSource element in source) { if (otherElement) throw new InvalidOperationException (); if (!otherElement) otherElement = true; singleElement = element; } return singleElement; } public static TSource SingleOrDefault (this IEnumerable source, Func predicate) { Check.SourceAndPredicate (source, predicate); bool otherElement = false; TSource singleElement = default (TSource); foreach (TSource element in source) { if (predicate (element)) { if (otherElement) throw new InvalidOperationException (); if (!otherElement) otherElement = true; singleElement = element; } } return singleElement; } #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) { int i = 0; foreach (var element in source) { if (i++ < count) continue; yield return element; } } #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) { return Sum (source, (a, b) => a + b); } public static int? Sum (this IEnumerable source) { return SumNullable (source, (a, b) => a.HasValue ? a + b : a); } public static int Sum (this IEnumerable source, Func selector) { return Sum (source, (a, b) => a + selector (b)); } public static int? Sum (this IEnumerable source, Func selector) { return SumNullable (source, (a, b) => { var value = selector (b); return value.HasValue ? a + value.Value : a; }); } public static long Sum (this IEnumerable source) { return Sum (source, (a, b) => a + b); } public static long? Sum (this IEnumerable source) { return SumNullable (source, (a, b) => a.HasValue ? a + b : a); } public static long Sum (this IEnumerable source, Func selector) { return Sum (source, (a, b) => a + selector (b)); } public static long? Sum (this IEnumerable source, Func selector) { return SumNullable (source, (a, b) => { var value = selector (b); return value.HasValue ? a + value.Value : a; }); } public static double Sum (this IEnumerable source) { return Sum (source, (a, b) => a + b); } public static double? Sum (this IEnumerable source) { return SumNullable (source, (a, b) => a.HasValue ? a + b : a); } public static double Sum (this IEnumerable source, Func selector) { return Sum (source, (a, b) => a + selector (b)); } public static double? Sum (this IEnumerable source, Func selector) { return SumNullable (source, (a, b) => { var value = selector (b); return value.HasValue ? a + value.Value : a; }); } public static float Sum (this IEnumerable source) { return Sum (source, (a, b) => a + b); } public static float? Sum (this IEnumerable source) { return SumNullable (source, (a, b) => a.HasValue ? a + b : a); } public static float Sum (this IEnumerable source, Func selector) { return Sum (source, (a, b) => a + selector (b)); } public static float? Sum (this IEnumerable source, Func selector) { return SumNullable (source, (a, b) => { var value = selector (b); return value.HasValue ? a + value.Value : a; }); } public static decimal Sum (this IEnumerable source) { return Sum (source, (a, b) => a + b); } public static decimal? Sum (this IEnumerable source) { return SumNullable (source, (a, b) => a.HasValue ? a + b : a); } public static decimal Sum (this IEnumerable source, Func selector) { return Sum (source, (a, b) => a + selector (b)); } public static decimal? Sum (this IEnumerable source, Func selector) { return SumNullable (source, (a, b) => { var value = selector (b); return value.HasValue ? a + value.Value : a; }); } static TR Sum (this IEnumerable source, Func func) { Check.Source (source); TR total = default (TR); int counter = 0; foreach (var element in source) { total = func (total, element); ++counter; } if (counter == 0) throw new InvalidOperationException (); return total; } static TR SumNullable (this IEnumerable source, Func func) { Check.Source (source); TR total = default (TR); foreach (var element in source) { total = func (total, element); } 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) { if (counter++ == count) yield break; yield return element; } } #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 (TSource 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); List list = new List (source); return list.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); 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) { throw new NotImplementedException (); // FIXME: compiler issue //return ToDictionary (source, keySelector, (a) => a, 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, null); } public static ILookup ToLookup (this IEnumerable source, Func keySelector, IEqualityComparer comparer) { Check.SourceAndKeySelector (source, keySelector); var dictionary = new Dictionary> (comparer ?? EqualityComparer.Default); foreach (TSource element in source) { TKey key = keySelector (element); if (key == null) throw new ArgumentNullException (); if (!dictionary.ContainsKey (key)) dictionary.Add (key, new List ()); dictionary [key].Add (element); } return new Lookup (dictionary); } 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); Dictionary> dictionary = new Dictionary> (comparer ?? EqualityComparer.Default); foreach (TSource element in source) { TKey key = keySelector (element); if (key == null) throw new ArgumentNullException (); if (!dictionary.ContainsKey (key)) dictionary.Add (key, new List ()); dictionary [key].Add (elementSelector (element)); } return new Lookup (dictionary); } #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; var first_enumerator = first.GetEnumerator (); var 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, comparer)) { items.Add (element); yield return element; } } } #endregion #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 new ReadOnlyCollection (new TSource [0]); // could we singlotenize that? var ro = source as ReadOnlyCollection; if (ro != null) return ro; return new ReadOnlyCollection (source.ToArray ()); } } }