#region License, Terms and Author(s)
//
// LINQBridge
// Copyright (c) 2007 Atif Aziz, Joseph Albahari. All rights reserved.
//
// Author(s):
//
// Atif Aziz, http://www.raboof.com
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the New BSD License, a copy of which should have
// been delivered along with this distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#endregion
// $Id: Enumerable.cs c08984d432b1 2012/04/17 16:05:19 azizatif $
namespace System.Linq
{
#region Imports
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using LinqBridge;
#endregion
///
/// Provides a set of static (Shared in Visual Basic) methods for
/// querying objects that implement .
///
static partial class Enumerable
{
///
/// Returns the input typed as .
///
public static IEnumerable AsEnumerable(this IEnumerable source)
{
return source;
}
///
/// Returns an empty that has the
/// specified type argument.
///
public static IEnumerable Empty()
{
return Sequence.Empty;
}
///
/// Converts the elements of an to the
/// specified type.
///
public static IEnumerable Cast(
this IEnumerable source)
{
if (source == null) throw new ArgumentNullException("source");
return CastYield(source);
}
private static IEnumerable CastYield(
IEnumerable source)
{
foreach (var item in source)
yield return (TResult)item;
}
///
/// Filters the elements of an based on a specified type.
///
public static IEnumerable OfType(
this IEnumerable source)
{
if (source == null) throw new ArgumentNullException("source");
return OfTypeYield(source);
}
private static IEnumerable OfTypeYield(
IEnumerable source)
{
foreach (var item in source)
if (item is TResult)
yield return (TResult)item;
}
///
/// Generates a sequence of integral numbers within a specified range.
///
/// The value of the first integer in the sequence.
/// The number of sequential integers to generate.
public static IEnumerable Range(int start, int count)
{
if (count < 0)
throw new ArgumentOutOfRangeException("count", count, null);
var end = (long)start + count;
if (end - 1 >= int.MaxValue)
throw new ArgumentOutOfRangeException("count", count, null);
return RangeYield(start, end);
}
private static IEnumerable RangeYield(int start, long end)
{
for (var i = start; i < end; i++)
yield return i;
}
///
/// Generates a sequence that contains one repeated value.
///
public static IEnumerable Repeat(TResult element, int count)
{
if (count < 0) throw new ArgumentOutOfRangeException("count", count, null);
return RepeatYield(element, count);
}
private static IEnumerable RepeatYield(TResult element, int count)
{
for (var i = 0; i < count; i++)
yield return element;
}
///
/// Filters a sequence of values based on a predicate.
///
public static IEnumerable Where(
this IEnumerable source,
FFunc predicate)
{
if (predicate == null) throw new ArgumentNullException("predicate");
return source.Where((item, i) => predicate(item));
}
///
/// Filters a sequence of values based on a predicate.
/// Each element's index is used in the logic of the predicate function.
///
public static IEnumerable Where(
this IEnumerable source,
FFunc predicate)
{
if (source == null) throw new ArgumentNullException("source");
if (predicate == null) throw new ArgumentNullException("predicate");
return WhereYield(source, predicate);
}
private static IEnumerable WhereYield(
IEnumerable source,
FFunc predicate)
{
var i = 0;
foreach (var item in source)
if (predicate(item, i++))
yield return item;
}
///
/// Projects each element of a sequence into a new form.
///
public static IEnumerable Select(
this IEnumerable source,
FFunc selector)
{
if (selector == null) throw new ArgumentNullException("selector");
return source.Select((item, i) => selector(item));
}
///
/// Projects each element of a sequence into a new form by
/// incorporating the element's index.
///
public static IEnumerable Select(
this IEnumerable source,
FFunc selector)
{
if (source == null) throw new ArgumentNullException("source");
if (selector == null) throw new ArgumentNullException("selector");
return SelectYield(source, selector);
}
private static IEnumerable SelectYield(
IEnumerable source,
FFunc selector)
{
var i = 0;
foreach (var item in source)
yield return selector(item, i++);
}
///
/// Projects each element of a sequence to an
/// and flattens the resulting sequences into one sequence.
///
public static IEnumerable SelectMany(
this IEnumerable source,
FFunc> selector)
{
if (selector == null) throw new ArgumentNullException("selector");
return source.SelectMany((item, i) => selector(item));
}
///
/// Projects each element of a sequence to an ,
/// and flattens the resulting sequences into one sequence. The
/// index of each source element is used in the projected form of
/// that element.
///
public static IEnumerable SelectMany(
this IEnumerable source,
FFunc> selector)
{
if (selector == null) throw new ArgumentNullException("selector");
return source.SelectMany(selector, (item, subitem) => subitem);
}
///
/// Projects each element of a sequence to an ,
/// flattens the resulting sequences into one sequence, and invokes
/// a result selector function on each element therein.
///
public static IEnumerable SelectMany(
this IEnumerable source,
FFunc> collectionSelector,
FFunc resultSelector)
{
if (collectionSelector == null) throw new ArgumentNullException("collectionSelector");
return source.SelectMany((item, i) => collectionSelector(item), resultSelector);
}
///
/// Projects each element of a sequence to an ,
/// flattens the resulting sequences into one sequence, and invokes
/// a result selector function on each element therein. The index of
/// each source element is used in the intermediate projected form
/// of that element.
///
public static IEnumerable SelectMany(
this IEnumerable source,
FFunc> collectionSelector,
FFunc resultSelector)
{
if (source == null) throw new ArgumentNullException("source");
if (collectionSelector == null) throw new ArgumentNullException("collectionSelector");
if (resultSelector == null) throw new ArgumentNullException("resultSelector");
return SelectManyYield(source, collectionSelector, resultSelector);
}
private static IEnumerable SelectManyYield(
this IEnumerable source,
FFunc> collectionSelector,
FFunc resultSelector)
{
var i = 0;
foreach (var item in source)
foreach (var subitem in collectionSelector(item, i++))
yield return resultSelector(item, subitem);
}
///
/// Returns elements from a sequence as long as a specified condition is true.
///
public static IEnumerable TakeWhile(
this IEnumerable source,
FFunc predicate)
{
if (predicate == null) throw new ArgumentNullException("predicate");
return source.TakeWhile((item, i) => predicate(item));
}
///
/// Returns elements from a sequence as long as a specified condition is true.
/// The element's index is used in the logic of the predicate function.
///
public static IEnumerable TakeWhile(
this IEnumerable source,
FFunc predicate)
{
if (source == null) throw new ArgumentNullException("source");
if (predicate == null) throw new ArgumentNullException("predicate");
return TakeWhileYield(source, predicate);
}
private static IEnumerable TakeWhileYield(
this IEnumerable source,
FFunc predicate)
{
var i = 0;
foreach (var item in source)
if (predicate(item, i++))
yield return item;
else
break;
}
///
/// Returns a specified number of contiguous elements from the start
/// of a sequence.
///
public static IEnumerable Take(
this IEnumerable source,
int count)
{
return source.TakeWhile((item, i) => i < count);
}
private static class Futures
{
public static readonly FFunc Default = () => default(T);
public static readonly FFunc Undefined = () => { throw new InvalidOperationException(); };
}
///
/// Base implementation of First operator.
///
private static TSource FirstImpl(
this IEnumerable source,
FFunc empty)
{
if (source == null) throw new ArgumentNullException("source");
Debug.Assert(empty != null);
var list = source as IList; // optimized case for lists
if (list != null)
return list.Count > 0 ? list[0] : empty();
using (var e = source.GetEnumerator()) // fallback for enumeration
return e.MoveNext() ? e.Current : empty();
}
///
/// Returns the first element of a sequence.
///
public static TSource First(
this IEnumerable source)
{
return source.FirstImpl(Futures.Undefined);
}
///
/// Returns the first element in a sequence that satisfies a specified condition.
///
public static TSource First(
this IEnumerable source,
FFunc predicate)
{
return First(source.Where(predicate));
}
///
/// Returns the first element of a sequence, or a default value if
/// the sequence contains no elements.
///
public static TSource FirstOrDefault(
this IEnumerable source)
{
return source.FirstImpl(Futures.Default);
}
///
/// Returns the first element of the sequence that satisfies a
/// condition or a default value if no such element is found.
///
public static TSource FirstOrDefault(
this IEnumerable source,
FFunc predicate)
{
return FirstOrDefault(source.Where(predicate));
}
///
/// Base implementation of Last operator.
///
private static TSource LastImpl(
this IEnumerable source,
FFunc empty)
{
if (source == null) throw new ArgumentNullException("source");
var list = source as IList; // optimized case for lists
if (list != null)
return list.Count > 0 ? list[list.Count - 1] : empty();
using (var e = source.GetEnumerator())
{
if (!e.MoveNext())
return empty();
var last = e.Current;
while (e.MoveNext())
last = e.Current;
return last;
}
}
///
/// Returns the last element of a sequence.
///
public static TSource Last(
this IEnumerable source)
{
return source.LastImpl(Futures.Undefined);
}
///
/// Returns the last element of a sequence that satisfies a
/// specified condition.
///
public static TSource Last(
this IEnumerable source,
FFunc predicate)
{
return Last(source.Where(predicate));
}
///
/// Returns the last element of a sequence, or a default value if
/// the sequence contains no elements.
///
public static TSource LastOrDefault(
this IEnumerable source)
{
return source.LastImpl(Futures.Default);
}
///
/// Returns the last element of a sequence that satisfies a
/// condition or a default value if no such element is found.
///
public static TSource LastOrDefault(
this IEnumerable source,
FFunc predicate)
{
return LastOrDefault(source.Where(predicate));
}
///
/// Base implementation of Single operator.
///
private static TSource SingleImpl(
this IEnumerable source,
FFunc empty)
{
if (source == null) throw new ArgumentNullException("source");
using (var e = source.GetEnumerator())
{
if (e.MoveNext())
{
var single = e.Current;
if (!e.MoveNext())
return single;
throw new InvalidOperationException();
}
return empty();
}
}
///
/// Returns the only element of a sequence, and throws an exception
/// if there is not exactly one element in the sequence.
///
public static TSource Single(
this IEnumerable source)
{
return source.SingleImpl(Futures.Undefined);
}
///
/// Returns the only element of a sequence that satisfies a
/// specified condition, and throws an exception if more than one
/// such element exists.
///
public static TSource Single(
this IEnumerable source,
FFunc predicate)
{
return Single(source.Where(predicate));
}
///
/// Returns the only element of a sequence, or a default value if
/// the sequence is empty; this method throws an exception if there
/// is more than one element in the sequence.
///
public static TSource SingleOrDefault(
this IEnumerable source)
{
return source.SingleImpl(Futures.Default);
}
///
/// Returns the only element of a sequence that satisfies a
/// specified condition or a default value if no such element
/// exists; this method throws an exception if more than one element
/// satisfies the condition.
///
public static TSource SingleOrDefault(
this IEnumerable source,
FFunc predicate)
{
return SingleOrDefault(source.Where(predicate));
}
///
/// Returns the element at a specified index in a sequence.
///
public static TSource ElementAt(
this IEnumerable source,
int index)
{
if (source == null) throw new ArgumentNullException("source");
if (index < 0)
throw new ArgumentOutOfRangeException("index", index, null);
var list = source as IList;
if (list != null)
return list[index];
try
{
return source.SkipWhile((item, i) => i < index).First();
}
catch (InvalidOperationException) // if thrown by First
{
throw new ArgumentOutOfRangeException("index", index, null);
}
}
///
/// Returns the element at a specified index in a sequence or a
/// default value if the index is out of range.
///
public static TSource ElementAtOrDefault(
this IEnumerable source,
int index)
{
if (source == null) throw new ArgumentNullException("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.SkipWhile((item, i) => i < index).FirstOrDefault();
}
///
/// Inverts the order of the elements in a sequence.
///
public static IEnumerable Reverse(
this IEnumerable source)
{
if (source == null) throw new ArgumentNullException("source");
return ReverseYield(source);
}
private static IEnumerable ReverseYield(IEnumerable source)
{
var stack = new Stack();
foreach (var item in source)
stack.Push(item);
foreach (var item in stack)
yield return item;
}
///
/// Bypasses elements in a sequence as long as a specified condition
/// is true and then returns the remaining elements.
///
public static IEnumerable SkipWhile(
this IEnumerable source,
FFunc predicate)
{
if (predicate == null) throw new ArgumentNullException("predicate");
return source.SkipWhile((item, i) => predicate(item));
}
///
/// Bypasses elements in a sequence as long as a specified condition
/// is true and then returns the remaining elements. The element's
/// index is used in the logic of the predicate function.
///
public static IEnumerable SkipWhile(
this IEnumerable source,
FFunc predicate)
{
if (source == null) throw new ArgumentNullException("source");
if (predicate == null) throw new ArgumentNullException("predicate");
return SkipWhileYield(source, predicate);
}
private static IEnumerable SkipWhileYield(
IEnumerable source,
FFunc predicate)
{
using (var e = source.GetEnumerator())
{
for (var i = 0; ; i++)
{
if (!e.MoveNext())
yield break;
if (!predicate(e.Current, i))
break;
}
do { yield return e.Current; } while (e.MoveNext());
}
}
///
/// Bypasses a specified number of elements in a sequence and then
/// returns the remaining elements.
///
public static IEnumerable Skip(
this IEnumerable source,
int count)
{
return source.SkipWhile((item, i) => i < count);
}
///
/// Returns the number of elements in a sequence.
///
public static int Count(
this IEnumerable source)
{
if (source == null) throw new ArgumentNullException("source");
var collection = source as ICollection;
return collection != null
? collection.Count
: source.Aggregate(0, (count, item) => checked(count + 1));
}
///
/// Returns a number that represents how many elements in the
/// specified sequence satisfy a condition.
///
public static int Count(
this IEnumerable source,
FFunc predicate)
{
return Count(source.Where(predicate));
}
///
/// Returns an that represents the total number
/// of elements in a sequence.
///
public static long LongCount(
this IEnumerable source)
{
if (source == null) throw new ArgumentNullException("source");
var array = source as Array;
return array != null
? array.LongLength
: source.Aggregate(0L, (count, item) => count + 1);
}
///
/// Returns an that represents how many elements
/// in a sequence satisfy a condition.
///
public static long LongCount(
this IEnumerable source,
FFunc predicate)
{
return LongCount(source.Where(predicate));
}
///
/// Concatenates two sequences.
///
public static IEnumerable Concat(
this IEnumerable first,
IEnumerable second)
{
if (first == null) throw new ArgumentNullException("first");
if (second == null) throw new ArgumentNullException("second");
return ConcatYield(first, second);
}
private static IEnumerable ConcatYield(
IEnumerable first,
IEnumerable second)
{
foreach (var item in first)
yield return item;
foreach (var item in second)
yield return item;
}
///
/// Creates a from an .
///
public static List ToList(
this IEnumerable source)
{
if (source == null) throw new ArgumentNullException("source");
return new List(source);
}
///
/// Creates an array from an .
///
public static TSource[] ToArray(
this IEnumerable source)
{
return source.ToList().ToArray();
}
///
/// Returns distinct elements from a sequence by using the default
/// equality comparer to compare values.
///
public static IEnumerable Distinct(
this IEnumerable source)
{
return Distinct(source, /* comparer */ null);
}
///
/// Returns distinct elements from a sequence by using a specified
/// to compare values.
///
public static IEnumerable Distinct(
this IEnumerable source,
IEqualityComparer comparer)
{
if (source == null) throw new ArgumentNullException("source");
return DistinctYield(source, comparer);
}
private static IEnumerable DistinctYield(
IEnumerable source,
IEqualityComparer comparer)
{
var set = new Dictionary(comparer);
var gotNull = false;
foreach (var item in source)
{
if (item == null)
{
if (gotNull)
continue;
gotNull = true;
}
else
{
if (set.ContainsKey(item))
continue;
set.Add(item, null);
}
yield return item;
}
}
///
/// Creates a from an
/// according to a specified key
/// selector function.
///
public static ILookup ToLookup(
this IEnumerable source,
FFunc keySelector)
{
return ToLookup(source, keySelector, e => e, /* comparer */ null);
}
///
/// Creates a from an
/// according to a specified key
/// selector function and a key comparer.
///
public static ILookup ToLookup(
this IEnumerable source,
FFunc keySelector,
IEqualityComparer comparer)
{
return ToLookup(source, keySelector, e => e, comparer);
}
///
/// Creates a from an
/// according to specified key
/// and element selector functions.
///
public static ILookup ToLookup(
this IEnumerable source,
FFunc keySelector,
FFunc elementSelector)
{
return ToLookup(source, keySelector, elementSelector, /* comparer */ null);
}
///
/// Creates a from an
/// according to a specified key
/// selector function, a comparer and an element selector function.
///
public static ILookup ToLookup(
this IEnumerable source,
FFunc keySelector,
FFunc elementSelector,
IEqualityComparer comparer)
{
if (source == null) throw new ArgumentNullException("source");
if (keySelector == null) throw new ArgumentNullException("keySelector");
if (elementSelector == null) throw new ArgumentNullException("elementSelector");
var lookup = new Lookup(comparer);
foreach (var item in source)
{
var key = keySelector(item);
var grouping = (Grouping)lookup.Find(key);
if (grouping == null)
{
grouping = new Grouping(key);
lookup.Add(grouping);
}
grouping.Add(elementSelector(item));
}
return lookup;
}
///
/// Groups the elements of a sequence according to a specified key
/// selector function.
///
public static IEnumerable> GroupBy(
this IEnumerable source,
FFunc keySelector)
{
return GroupBy(source, keySelector, /* comparer */ null);
}
///
/// Groups the elements of a sequence according to a specified key
/// selector function and compares the keys by using a specified
/// comparer.
///
public static IEnumerable> GroupBy(
this IEnumerable source,
FFunc keySelector,
IEqualityComparer comparer)
{
return GroupBy(source, keySelector, e => e, comparer);
}
///
/// Groups the elements of a sequence according to a specified key
/// selector function and projects the elements for each group by
/// using a specified function.
///
public static IEnumerable> GroupBy(
this IEnumerable source,
FFunc keySelector,
FFunc elementSelector)
{
return GroupBy(source, keySelector, elementSelector, /* comparer */ null);
}
///
/// Groups the elements of a sequence according to a specified key
/// selector function and creates a result value from each group and
/// its key.
///
public static IEnumerable> GroupBy(
this IEnumerable source,
FFunc keySelector,
FFunc elementSelector,
IEqualityComparer comparer)
{
if (source == null) throw new ArgumentNullException("source");
if (keySelector == null) throw new ArgumentNullException("keySelector");
if (elementSelector == null) throw new ArgumentNullException("elementSelector");
return ToLookup(source, keySelector, elementSelector, comparer);
}
///
/// Groups the elements of a sequence according to a key selector
/// function. The keys are compared by using a comparer and each
/// group's elements are projected by using a specified function.
///
public static IEnumerable GroupBy(
this IEnumerable source,
FFunc keySelector,
FFunc, TResult> resultSelector)
{
return GroupBy(source, keySelector, resultSelector, /* comparer */ null);
}
///
/// Groups the elements of a sequence according to a specified key
/// selector function and creates a result value from each group and
/// its key. The elements of each group are projected by using a
/// specified function.
///
public static IEnumerable GroupBy(
this IEnumerable source,
FFunc keySelector,
FFunc, TResult> resultSelector,
IEqualityComparer comparer)
{
if (source == null) throw new ArgumentNullException("source");
if (keySelector == null) throw new ArgumentNullException("keySelector");
if (resultSelector == null) throw new ArgumentNullException("resultSelector");
return ToLookup(source, keySelector, comparer).Select(g => resultSelector(g.Key, g));
}
///
/// Groups the elements of a sequence according to a specified key
/// selector function and creates a result value from each group and
/// its key. The keys are compared by using a specified comparer.
///
public static IEnumerable GroupBy(
this IEnumerable