// (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; namespace System.Windows.Controls.DataVisualization { /// /// This class contains general purpose functions to manipulate the generic /// IEnumerable type. /// internal static class EnumerableFunctions { /// /// Attempts to cast IEnumerable to a list in order to retrieve a count /// in order one. It attempts to cast fail the sequence is enumerated. /// /// The sequence. /// The number of elements in the sequence. public static int FastCount(this IEnumerable that) { IList list = that as IList; if (list != null) { return list.Count; } return that.CastWrapper().Count(); } /// /// Returns the minimum value in the stream based on the result of a /// project function. /// /// The stream type. /// The stream. /// The function that transforms the /// item. /// The minimum value or null. public static T MinOrNull(this IEnumerable that, Func projectionFunction) where T : class { IComparable result = null; T minimum = default(T); if (!that.Any()) { return minimum; } minimum = that.First(); result = projectionFunction(minimum); foreach (T item in that.Skip(1)) { IComparable currentResult = projectionFunction(item); if (result.CompareTo(currentResult) > 0) { result = currentResult; minimum = item; } } return minimum; } /// /// Returns the sum of all values in the sequence or the default value. /// /// The stream. /// The sum of all values or the default value. public static double SumOrDefault(this IEnumerable that) { if (!that.Any()) { return 0.0; } else { return that.Sum(); } } /// /// Returns the maximum value in the stream based on the result of a /// project function. /// /// The stream type. /// The stream. /// The function that transforms the /// item. /// The maximum value or null. public static T MaxOrNull(this IEnumerable that, Func projectionFunction) where T : class { IComparable result = null; T maximum = default(T); if (!that.Any()) { return maximum; } maximum = that.First(); result = projectionFunction(maximum); foreach (T item in that.Skip(1)) { IComparable currentResult = projectionFunction(item); if (result.CompareTo(currentResult) < 0) { result = currentResult; maximum = item; } } return maximum; } /// /// Accepts two sequences and applies a function to the corresponding /// values in the two sequences. /// /// The type of the first sequence. /// The type of the second sequence. /// The return type of the function. /// The first sequence. /// The second sequence. /// The function to apply to the corresponding values /// from the two sequences. /// A sequence of transformed values from both sequences. public static IEnumerable Zip(IEnumerable enumerable0, IEnumerable enumerable1, Func func) { IEnumerator enumerator0 = enumerable0.GetEnumerator(); IEnumerator enumerator1 = enumerable1.GetEnumerator(); while (enumerator0.MoveNext() && enumerator1.MoveNext()) { yield return func(enumerator0.Current, enumerator1.Current); } } /// /// Creates a sequence of values by accepting an initial value, an /// iteration function, and apply the iteration function recursively. /// /// The type of the sequence. /// The initial value. /// The function to apply to the value. /// /// A sequence of the iterated values. public static IEnumerable Iterate(T value, Func nextFunction) { yield return value; while (true) { value = nextFunction(value); yield return value; } } /// /// Returns the index of an item in a sequence. /// /// The sequence. /// The item to search for. /// The index of the item or -1 if not found. public static int IndexOf(this IEnumerable that, object value) { int index = 0; foreach (object item in that) { if (object.ReferenceEquals(value, item) || value.Equals(item)) { return index; } index++; } return -1; } /// /// Executes an action for each item and a sequence, passing in the /// index of that item to the action procedure. /// /// The type of the sequence. /// The sequence. /// A function that accepts a sequence item and its /// index in the sequence. public static void ForEachWithIndex(this IEnumerable that, Action action) { int index = 0; foreach (T item in that) { action(item, index); index++; } } /// /// Returns the maximum value or null if sequence is empty. /// /// The type of the sequence. /// The sequence to retrieve the maximum value from. /// /// The maximum value or null. public static T? MaxOrNullable(this IEnumerable that) where T : struct, IComparable { if (!that.Any()) { return null; } return that.Max(); } /// /// Returns the minimum value or null if sequence is empty. /// /// The type of the sequence. /// The sequence to retrieve the minimum value from. /// /// The minimum value or null. public static T? MinOrNullable(this IEnumerable that) where T : struct, IComparable { if (!that.Any()) { return null; } return that.Min(); } /// /// Attempts to retrieve an element at an index by testing whether a /// sequence is randomly accessible. If not, performance degrades to a /// linear search. /// /// The type of the elements in the sequence. /// The sequence. /// The index of the element in the sequence. /// The element at the given index. public static T FastElementAt(this IEnumerable that, int index) { { IList list = that as IList; if (list != null) { return list[index]; } } { IList list = that as IList; if (list != null) { return (T) list[index]; } } return that.CastWrapper().ElementAt(index); } /// /// Applies an accumulator function over a sequence and returns each intermediate result. /// /// Type of elements in source sequence. /// Type of elements in result sequence. /// Sequence to scan. /// Initial accumulator value. /// Function used to generate the result sequence. /// Sequence of intermediate results. public static IEnumerable Scan(this IEnumerable that, S seed, Func accumulator) { S value = seed; yield return seed; foreach (T t in that) { value = accumulator(value, t); yield return value; } yield break; } /// /// Converts the elements of an System.Collections.IEnumerable to the specified type. /// /// /// A wrapper for the Enumerable.Cast(T) method that works around a limitation on some platforms. /// /// The type to convert the elements of source to. /// The System.Collections.IEnumerable that contains the elements to be converted. /// /// An System.Collections.Generic.IEnumerable(T) that contains each element of the source sequence converted to the specified type. /// public static IEnumerable CastWrapper(this IEnumerable source) { #if SILVERLIGHT // Certain flavors of this platform have a bug which causes Cast to raise an exception incorrectly. // Work around that by using the more general OfType method instead for no loss of functionality. return source.OfType(); #else // No issues on this platform - call directly through to Cast return source.Cast(); #endif } } }