/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Linq; namespace QuantConnect.Util { /// /// Provides more extension methods for the enumerable types /// public static class LinqExtensions { /// /// Creates a new read-only dictionary from the key value pairs /// /// The key type /// The value type /// The IEnumerable of KeyValuePair instances to convert to a dictionary /// A read-only dictionary holding the same data as the enumerable public static IReadOnlyDictionary ToReadOnlyDictionary(this IEnumerable> enumerable) { return new ReadOnlyDictionary(enumerable.ToDictionary()); } /// /// Creates a new from the elements in the specified enumerable /// /// The item type of the source enumerable /// The type of the items in the output /// The items to be placed into the enumerable /// Selects items from the enumerable to be placed into the /// A new containing the items in the enumerable public static HashSet ToHashSet(this IEnumerable enumerable, Func selector) { return new HashSet(enumerable.Select(selector)); } /// /// Creates a new from the projected elements in the specified enumerable /// /// The item type of the source enumerable /// The type of the items in the output /// The items to be placed into the list /// Selects items from the enumerable to be placed into the /// A new containing the items in the enumerable public static List ToList(this IEnumerable enumerable, Func selector) { return enumerable.Select(selector).ToList(); } /// /// Creates a new array from the projected elements in the specified enumerable /// /// The item type of the source enumerable /// The type of the items in the output array /// The items to be placed into the array /// Selects items from the enumerable to be placed into the array /// A new array containing the items in the enumerable public static TResult[] ToArray(this IEnumerable enumerable, Func selector) { return enumerable.Select(selector).ToArray(); } /// /// Creates a new immutable array from the projected elements in the specified enumerable /// /// The item type of the source enumerable /// The type of the items in the output array /// The items to be placed into the array /// Selects items from the enumerable to be placed into the array /// A new array containing the items in the enumerable public static ImmutableArray ToImmutableArray(this IEnumerable enumerable, Func selector) { return enumerable.Select(selector).ToImmutableArray(); } /// /// Returns true if the specified enumerable is null or has no elements /// /// The enumerable's item type /// The enumerable to check for a value /// True if the enumerable has elements, false otherwise public static bool IsNullOrEmpty(this IEnumerable enumerable) { return enumerable == null || !enumerable.Any(); } /// /// Gets the median value in the collection /// /// The item type in the collection /// The enumerable of items to search /// The median value, throws InvalidOperationException if no items are present public static T Median(this IEnumerable enumerable) { var collection = enumerable.ToList(); return collection.OrderBy(x => x).Skip(collection.Count/2).First(); } /// /// Gets the median value in the collection /// /// The item type in the collection /// The type of the value selected /// The collection of items to search /// Function used to select a value from collection items /// The median value, throws InvalidOperationException if no items are present public static TProperty Median(this IEnumerable collection, Func selector) { return collection.Select(selector).Median(); } /// /// Performs a binary search on the specified collection. /// /// The type of the item. /// The type of the searched item. /// The list to be searched. /// The value to search for. /// The comparer that is used to compare the value with the list items. /// The index of the item if found, otherwise the bitwise complement where the value should be per MSDN specs public static int BinarySearch(this IList list, TSearch value, Func comparer) { if (list == null) { throw new ArgumentNullException(nameof(list)); } if (comparer == null) { throw new ArgumentNullException(nameof(comparer)); } var lower = 0; var upper = list.Count - 1; while (lower <= upper) { var middle = lower + (upper - lower) / 2; var comparisonResult = comparer(value, list[middle]); if (comparisonResult < 0) { upper = middle - 1; } else if (comparisonResult > 0) { lower = middle + 1; } else { return middle; } } return ~lower; } /// /// Performs a binary search on the specified collection. /// /// The type of the item. /// The list to be searched. /// The value to search for. /// The index of the item if found, otherwise the bitwise complement where the value should be per MSDN specs public static int BinarySearch(this IList list, TItem value) { return BinarySearch(list, value, Comparer.Default); } /// /// Performs a binary search on the specified collection. /// /// The type of the item. /// The list to be searched. /// The value to search for. /// The comparer that is used to compare the value with the list items. /// The index of the item if found, otherwise the bitwise complement where the value should be per MSDN specs public static int BinarySearch(this IList list, TItem value, IComparer comparer) { return list.BinarySearch(value, comparer.Compare); } /// /// Wraps the specified enumerable such that it will only be enumerated once /// /// The enumerable's element type /// The source enumerable to be wrapped /// A new enumerable that can be enumerated multiple times without re-enumerating the source enumerable public static IEnumerable Memoize(this IEnumerable enumerable) { if (enumerable is MemoizingEnumerable) return enumerable; return new MemoizingEnumerable(enumerable); } /// /// Produces the an enumerable of the range of values between start and end using the specified /// incrementing function /// /// The enumerable item type /// The start of the range /// The end of the range, non-inclusive by default /// The incrementing function, with argument of the current item /// True to emit the end point, false otherwise /// An enumerable of the range of items between start and end public static IEnumerable Range(T start, T end, Func incrementer, bool includeEndPoint = false) where T : IComparable { var current = start; if (includeEndPoint) { while (current.CompareTo(end) <= 0) { yield return current; current = incrementer(current); } } else { while (current.CompareTo(end) < 0) { yield return current; current = incrementer(current); } } } /// /// Groups adjacent elements of the enumerale using the specified grouper function /// /// The enumerable item type /// The source enumerable to be grouped /// A function that accepts the previous value and the next value and returns /// true if the next value belongs in the same group as the previous value, otherwise returns false /// A new enumerable of the groups defined by grouper. These groups don't have a key /// and are only grouped by being emitted separately from this enumerable public static IEnumerable> GroupAdjacentBy(this IEnumerable enumerable, Func grouper) { using (var e = enumerable.GetEnumerator()) { if (e.MoveNext()) { var list = new List {e.Current}; var pred = e.Current; while (e.MoveNext()) { if (grouper(pred, e.Current)) { list.Add(e.Current); } else { yield return list; list = new List {e.Current}; } pred = e.Current; } yield return list; } } } /// /// Determines if there are any differences between the left and right collections. /// This method uses sets to improve performance and also uses lazy evaluation so if a /// difference is found, true is immediately returned and evaluation is halted. /// /// The item type /// The left set /// The right set /// True if there are any differences between the two sets, false otherwise public static bool AreDifferent(this ISet left, ISet right) { if(ReferenceEquals(left, right)) { return false; } return !left.SetEquals(right); } /// /// Converts an to an /// /// Collection element type /// The enumerator to convert to an enumerable /// An enumerable wrapping the specified enumerator public static IEnumerable AsEnumerable(this IEnumerator enumerator) { using (enumerator) { while (enumerator.MoveNext()) { yield return enumerator.Current; } } } /// /// Gets the value associated with the specified key or provided default value if key is not found. /// /// The key type /// The value type /// The dictionary instance /// Lookup key /// Default value /// Value associated with the specified key or default value public static V GetValueOrDefault(this IDictionary dictionary, K key, V defaultValue = default(V)) { V obj; return dictionary.TryGetValue(key, out obj) ? obj : defaultValue; } /// /// Performs an action for each element in collection source /// /// /// Collection source /// An action to perform public static void DoForEach(this IEnumerable source, Action action) { foreach (var element in source) { action(element); } } } }