// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using System.Threading; namespace System.Linq { public static partial class EnumerableEx { /// /// Hides the enumerable sequence object identity. /// /// Source sequence element type. /// Source sequence. /// Enumerable sequence with the same behavior as the original, but hiding the source object identity. /// AsEnumerable doesn't hide the object identity, and simply acts as a cast to the IEnumerable<TSource> interface. public static IEnumerable Hide(this IEnumerable source) { if (source == null) throw new ArgumentNullException("source"); return source.Hide_(); } private static IEnumerable Hide_(this IEnumerable source) { foreach (var item in source) yield return item; } /// /// Enumerates the sequence and invokes the given action for each value in the sequence. /// /// Source sequence element type. /// Source sequence. /// Action to invoke for each element. public static void ForEach(this IEnumerable source, Action onNext) { if (source == null) throw new ArgumentNullException("source"); if (onNext == null) throw new ArgumentNullException("onNext"); foreach (var item in source) onNext(item); } /// /// Enumerates the sequence and invokes the given action for each value in the sequence. /// /// Source sequence element type. /// Source sequence. /// Action to invoke for each element. public static void ForEach(this IEnumerable source, Action onNext) { if (source == null) throw new ArgumentNullException("source"); if (onNext == null) throw new ArgumentNullException("onNext"); var i = 0; foreach (var item in source) onNext(item, i++); } /// /// Lazily invokes an action for each value in the sequence. /// /// Source sequence element type. /// Source sequence. /// Action to invoke for each element. /// Sequence exhibiting the specified side-effects upon enumeration. public static IEnumerable Do(this IEnumerable source, Action onNext) { if (source == null) throw new ArgumentNullException("source"); if (onNext == null) throw new ArgumentNullException("onNext"); return DoHelper(source, onNext, _ => { }, () => { }); } /// /// Lazily invokes an action for each value in the sequence, and executes an action for successful termination. /// /// Source sequence element type. /// Source sequence. /// Action to invoke for each element. /// Action to invoke on successful termination of the sequence. /// Sequence exhibiting the specified side-effects upon enumeration. public static IEnumerable Do(this IEnumerable source, Action onNext, Action onCompleted) { if (source == null) throw new ArgumentNullException("source"); if (onNext == null) throw new ArgumentNullException("onNext"); if (onCompleted == null) throw new ArgumentNullException("onCompleted"); return DoHelper(source, onNext, _ => { }, onCompleted); } /// /// Lazily invokes an action for each value in the sequence, and executes an action upon exceptional termination. /// /// Source sequence element type. /// Source sequence. /// Action to invoke for each element. /// Action to invoke on exceptional termination of the sequence. /// Sequence exhibiting the specified side-effects upon enumeration. public static IEnumerable Do(this IEnumerable source, Action onNext, Action onError) { if (source == null) throw new ArgumentNullException("source"); if (onNext == null) throw new ArgumentNullException("onNext"); if (onError == null) throw new ArgumentNullException("onError"); return DoHelper(source, onNext, onError, () => { }); } /// /// Lazily invokes an action for each value in the sequence, and executes an action upon successful or exceptional termination. /// /// Source sequence element type. /// Source sequence. /// Action to invoke for each element. /// Action to invoke on exceptional termination of the sequence. /// Action to invoke on successful termination of the sequence. /// Sequence exhibiting the specified side-effects upon enumeration. public static IEnumerable Do(this IEnumerable source, Action onNext, Action onError, Action onCompleted) { if (source == null) throw new ArgumentNullException("source"); if (onNext == null) throw new ArgumentNullException("onNext"); if (onError == null) throw new ArgumentNullException("onError"); if (onCompleted == null) throw new ArgumentNullException("onCompleted"); return DoHelper(source, onNext, onError, onCompleted); } #if !NO_RXINTERFACES /// /// Lazily invokes observer methods for each value in the sequence, and upon successful or exceptional termination. /// /// Source sequence element type. /// Source sequence. /// Observer to invoke notification calls on. /// Sequence exhibiting the side-effects of observer method invocation upon enumeration. public static IEnumerable Do(this IEnumerable source, IObserver observer) { if (source == null) throw new ArgumentNullException("source"); if (observer == null) throw new ArgumentNullException("observer"); return DoHelper(source, observer.OnNext, observer.OnError, observer.OnCompleted); } #endif private static IEnumerable DoHelper(this IEnumerable source, Action onNext, Action onError, Action onCompleted) { using (var e = source.GetEnumerator()) { while (true) { var current = default(TSource); try { if (!e.MoveNext()) break; current = e.Current; } catch (Exception ex) { onError(ex); throw; } onNext(current); yield return current; } onCompleted(); } } /// /// Generates a sequence of non-overlapping adjacent buffers over the source sequence. /// /// Source sequence element type. /// Source sequence. /// Number of elements for allocated buffers. /// Sequence of buffers containing source sequence elements. public static IEnumerable> Buffer(this IEnumerable source, int count) { if (source == null) throw new ArgumentNullException("source"); if (count <= 0) throw new ArgumentOutOfRangeException("count"); return source.Buffer_(count, count); } /// /// Generates a sequence of buffers over the source sequence, with specified length and possible overlap. /// /// Source sequence element type. /// Source sequence. /// Number of elements for allocated buffers. /// Number of elements to skip between the start of consecutive buffers. /// Sequence of buffers containing source sequence elements. public static IEnumerable> Buffer(this IEnumerable source, int count, int skip) { if (source == null) throw new ArgumentNullException("source"); if (count <= 0) throw new ArgumentOutOfRangeException("count"); if (skip <= 0) throw new ArgumentOutOfRangeException("skip"); return source.Buffer_(count, skip); } private static IEnumerable> Buffer_(this IEnumerable source, int count, int skip) { var buffers = new Queue>(); var i = 0; foreach (var item in source) { if (i % skip == 0) buffers.Enqueue(new List(count)); foreach (var buffer in buffers) buffer.Add(item); if (buffers.Count > 0 && buffers.Peek().Count == count) yield return buffers.Dequeue(); i++; } while (buffers.Count > 0) yield return buffers.Dequeue(); } /// /// Ignores all elements in the source sequence. /// /// Source sequence element type. /// Source sequence. /// Source sequence without its elements. public static IEnumerable IgnoreElements(this IEnumerable source) { if (source == null) throw new ArgumentNullException("source"); return source.IgnoreElements_(); } private static IEnumerable IgnoreElements_(this IEnumerable source) { foreach (var item in source) ; yield break; } /// /// Returns elements with a distinct key value by using the default equality comparer to compare key values. /// /// Source sequence element type. /// Key type. /// Source sequence. /// Key selector. /// Sequence that contains the elements from the source sequence with distinct key values. public static IEnumerable Distinct(this IEnumerable source, Func keySelector) { if (source == null) throw new ArgumentNullException("source"); if (keySelector == null) throw new ArgumentNullException("keySelector"); return source.Distinct_(keySelector, EqualityComparer.Default); } /// /// Returns elements with a distinct key value by using the specified equality comparer to compare key values. /// /// Source sequence element type. /// Key type. /// Source sequence. /// Key selector. /// Comparer used to compare key values. /// Sequence that contains the elements from the source sequence with distinct key values. public static IEnumerable Distinct(this IEnumerable source, Func keySelector, IEqualityComparer comparer) { if (source == null) throw new ArgumentNullException("source"); if (keySelector == null) throw new ArgumentNullException("keySelector"); if (comparer == null) throw new ArgumentNullException("comparer"); return source.Distinct_(keySelector, comparer); } private static IEnumerable Distinct_(this IEnumerable source, Func keySelector, IEqualityComparer comparer) { var set = new HashSet(comparer); foreach (var item in source) { var key = keySelector(item); if (set.Add(key)) yield return item; } } #if NO_HASHSET class HashSet { private Dictionary _set; public HashSet(IEqualityComparer comparer) { _set = new Dictionary(comparer); } public bool Add(T value) { if (_set.ContainsKey(value)) return false; _set[value] = null; return true; } } #endif /// /// Returns consecutive distinct elements by using the default equality comparer to compare values. /// /// Source sequence element type. /// Source sequence. /// Sequence without adjacent non-distinct elements. public static IEnumerable DistinctUntilChanged(this IEnumerable source) { if (source == null) throw new ArgumentNullException("source"); return source.DistinctUntilChanged_(x => x, EqualityComparer.Default); } /// /// Returns consecutive distinct elements by using the specified equality comparer to compare values. /// /// Source sequence element type. /// Source sequence. /// Comparer used to compare values. /// Sequence without adjacent non-distinct elements. public static IEnumerable DistinctUntilChanged(this IEnumerable source, IEqualityComparer comparer) { if (source == null) throw new ArgumentNullException("source"); if (comparer == null) throw new ArgumentNullException("comparer"); return source.DistinctUntilChanged_(x => x, comparer); } /// /// Returns consecutive distinct elements based on a key value by using the specified equality comparer to compare key values. /// /// Source sequence element type. /// Key type. /// Source sequence. /// Key selector. /// Sequence without adjacent non-distinct elements. public static IEnumerable DistinctUntilChanged(this IEnumerable source, Func keySelector) { if (source == null) throw new ArgumentNullException("source"); if (keySelector == null) throw new ArgumentNullException("keySelector"); return source.DistinctUntilChanged_(keySelector, EqualityComparer.Default); } /// /// Returns consecutive distinct elements based on a key value by using the specified equality comparer to compare key values. /// /// Source sequence element type. /// Key type. /// Source sequence. /// Key selector. /// Comparer used to compare key values. /// Sequence without adjacent non-distinct elements. public static IEnumerable DistinctUntilChanged(this IEnumerable source, Func keySelector, IEqualityComparer comparer) { if (source == null) throw new ArgumentNullException("source"); if (keySelector == null) throw new ArgumentNullException("keySelector"); if (comparer == null) throw new ArgumentNullException("comparer"); return source.DistinctUntilChanged_(keySelector, comparer); } private static IEnumerable DistinctUntilChanged_(this IEnumerable source, Func keySelector, IEqualityComparer comparer) { var currentKey = default(TKey); var hasCurrentKey = false; foreach (var item in source) { var key = keySelector(item); var comparerEquals = false; if (hasCurrentKey) { comparerEquals = comparer.Equals(currentKey, key); } if (!hasCurrentKey || !comparerEquals) { hasCurrentKey = true; currentKey = key; yield return item; } } } /// /// Expands the sequence by recursively applying a selector function. /// /// Source sequence element type. /// Source sequence. /// Selector function to retrieve the next sequence to expand. /// Sequence with results from the recursive expansion of the source sequence. public static IEnumerable Expand(this IEnumerable source, Func> selector) { if (source == null) throw new ArgumentNullException("source"); if (selector == null) throw new ArgumentNullException("selector"); return source.Expand_(selector); } private static IEnumerable Expand_(this IEnumerable source, Func> selector) { var queue = new Queue>(); queue.Enqueue(source); while (queue.Count > 0) { var src = queue.Dequeue(); foreach (var item in src) { queue.Enqueue(selector(item)); yield return item; } } } /// /// Returns the source sequence prefixed with the specified value. /// /// Source sequence element type. /// Source sequence. /// Values to prefix the sequence with. /// Sequence starting with the specified prefix value, followed by the source sequence. public static IEnumerable StartWith(this IEnumerable source, params TSource[] values) { if (source == null) throw new ArgumentNullException("source"); return source.StartWith_(values); } private static IEnumerable StartWith_(this IEnumerable source, params TSource[] values) { foreach (var x in values) yield return x; foreach (var item in source) yield return item; } /// /// Generates a sequence of accumulated values by scanning the source sequence and applying an accumulator function. /// /// Source sequence element type. /// Accumulation type. /// Source sequence. /// Accumulator seed value. /// Accumulation function to apply to the current accumulation value and each element of the sequence. /// Sequence with all intermediate accumulation values resulting from scanning the sequence. public static IEnumerable Scan(this IEnumerable source, TAccumulate seed, Func accumulator) { if (source == null) throw new ArgumentNullException("source"); if (accumulator == null) throw new ArgumentNullException("accumulator"); return source.Scan_(seed, accumulator); } private static IEnumerable Scan_(this IEnumerable source, TAccumulate seed, Func accumulator) { var acc = seed; foreach (var item in source) { acc = accumulator(acc, item); yield return acc; } } /// /// Generates a sequence of accumulated values by scanning the source sequence and applying an accumulator function. /// /// Source sequence element type. /// Source sequence. /// Accumulation function to apply to the current accumulation value and each element of the sequence. /// Sequence with all intermediate accumulation values resulting from scanning the sequence. public static IEnumerable Scan(this IEnumerable source, Func accumulator) { if (source == null) throw new ArgumentNullException("source"); if (accumulator == null) throw new ArgumentNullException("accumulator"); return source.Scan_(accumulator); } private static IEnumerable Scan_(this IEnumerable source, Func accumulator) { var hasSeed = false; var acc = default(TSource); foreach (var item in source) { if (!hasSeed) { hasSeed = true; acc = item; continue; } acc = accumulator(acc, item); yield return acc; } } /// /// Returns a specified number of contiguous elements from the end of the sequence. /// /// Source sequence element type. /// Source sequence. /// The number of elements to take from the end of the sequence. /// Sequence with the specified number of elements counting from the end of the source sequence. public static IEnumerable TakeLast(this IEnumerable source, int count) { if (source == null) throw new ArgumentNullException("source"); if (count < 0) throw new ArgumentOutOfRangeException("count"); return source.TakeLast_(count); } private static IEnumerable TakeLast_(this IEnumerable source, int count) { var q = new Queue(count); foreach (var item in source) { if (q.Count >= count) q.Dequeue(); q.Enqueue(item); } while (q.Count > 0) yield return q.Dequeue(); } /// /// Bypasses a specified number of contiguous elements from the end of the sequence and returns the remaining elements. /// /// Source sequence element type. /// Source sequence. /// The number of elements to skip from the end of the sequence before returning the remaining elements. /// Sequence bypassing the specified number of elements counting from the end of the source sequence. public static IEnumerable SkipLast(this IEnumerable source, int count) { if (source == null) throw new ArgumentNullException("source"); if (count < 0) throw new ArgumentOutOfRangeException("count"); return source.SkipLast_(count); } private static IEnumerable SkipLast_(this IEnumerable source, int count) { var q = new Queue(); foreach (var x in source) { q.Enqueue(x); if (q.Count > count) yield return q.Dequeue(); } } /// /// Repeats and concatenates the source sequence infinitely. /// /// Source sequence element type. /// Source sequence. /// Sequence obtained by concatenating the source sequence to itself infinitely. public static IEnumerable Repeat(this IEnumerable source) { if (source == null) throw new ArgumentNullException("source"); return Repeat_(source); } private static IEnumerable Repeat_(IEnumerable source) { while (true) foreach (var item in source) yield return item; } /// /// Repeats and concatenates the source sequence the given number of times. /// /// Source sequence element type. /// Source sequence. /// Number of times to repeat the source sequence. /// Sequence obtained by concatenating the source sequence to itself the specified number of times. public static IEnumerable Repeat(this IEnumerable source, int count) { if (source == null) throw new ArgumentNullException("source"); if (count < 0) throw new ArgumentOutOfRangeException("count"); return Repeat_(source, count); } private static IEnumerable Repeat_(IEnumerable source, int count) { for (var i = 0; i < count; i++) foreach (var item in source) yield return item; } } }