/*
* 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);
}
}
}
}