Lean  $LEAN_TAG$
LinqExtensions.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
17 using System.Collections.Generic;
18 using System.Collections.Immutable;
19 using System.Collections.ObjectModel;
20 using System.Linq;
21 
22 namespace QuantConnect.Util
23 {
24  /// <summary>
25  /// Provides more extension methods for the enumerable types
26  /// </summary>
27  public static class LinqExtensions
28  {
29  /// <summary>
30  /// Creates a dictionary enumerable of key value pairs
31  /// </summary>
32  /// <typeparam name="K">The key type</typeparam>
33  /// <typeparam name="V">The value type</typeparam>
34  /// <param name="enumerable">The IEnumerable of KeyValuePair instances to convert to a dictionary</param>
35  /// <returns>A dictionary holding the same data as the enumerable</returns>
36  public static Dictionary<K, V> ToDictionary<K, V>(this IEnumerable<KeyValuePair<K, V>> enumerable)
37  {
38  return enumerable.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
39  }
40 
41  /// <summary>
42  /// Creates a new read-only dictionary from the key value pairs
43  /// </summary>
44  /// <typeparam name="K">The key type</typeparam>
45  /// <typeparam name="V">The value type</typeparam>
46  /// <param name="enumerable">The IEnumerable of KeyValuePair instances to convert to a dictionary</param>
47  /// <returns>A read-only dictionary holding the same data as the enumerable</returns>
48  public static IReadOnlyDictionary<K, V> ToReadOnlyDictionary<K, V>(this IEnumerable<KeyValuePair<K, V>> enumerable)
49  {
50  return new ReadOnlyDictionary<K, V>(enumerable.ToDictionary());
51  }
52 
53  /// <summary>
54  /// Creates a new <see cref="HashSet{T}"/> from the elements in the specified enumerable
55  /// </summary>
56  /// <typeparam name="T">The item type of the source enumerable</typeparam>
57  /// <typeparam name="TResult">The type of the items in the output <see cref="HashSet{T}"/></typeparam>
58  /// <param name="enumerable">The items to be placed into the enumerable</param>
59  /// <param name="selector">Selects items from the enumerable to be placed into the <see cref="HashSet{T}"/></param>
60  /// <returns>A new <see cref="HashSet{T}"/> containing the items in the enumerable</returns>
61  public static HashSet<TResult> ToHashSet<T, TResult>(this IEnumerable<T> enumerable, Func<T, TResult> selector)
62  {
63  return new HashSet<TResult>(enumerable.Select(selector));
64  }
65 
66  /// <summary>
67  /// Creates a new <see cref="IList{T}"/> from the projected elements in the specified enumerable
68  /// </summary>
69  /// <typeparam name="T">The item type of the source enumerable</typeparam>
70  /// <typeparam name="TResult">The type of the items in the output <see cref="List{T}"/></typeparam>
71  /// <param name="enumerable">The items to be placed into the list</param>
72  /// <param name="selector">Selects items from the enumerable to be placed into the <see cref="List{T}"/></param>
73  /// <returns>A new <see cref="List{T}"/> containing the items in the enumerable</returns>
74  public static List<TResult> ToList<T, TResult>(this IEnumerable<T> enumerable, Func<T, TResult> selector)
75  {
76  return enumerable.Select(selector).ToList();
77  }
78 
79  /// <summary>
80  /// Creates a new array from the projected elements in the specified enumerable
81  /// </summary>
82  /// <typeparam name="T">The item type of the source enumerable</typeparam>
83  /// <typeparam name="TResult">The type of the items in the output array</typeparam>
84  /// <param name="enumerable">The items to be placed into the array</param>
85  /// <param name="selector">Selects items from the enumerable to be placed into the array</param>
86  /// <returns>A new array containing the items in the enumerable</returns>
87  public static TResult[] ToArray<T, TResult>(this IEnumerable<T> enumerable, Func<T, TResult> selector)
88  {
89  return enumerable.Select(selector).ToArray();
90  }
91 
92  /// <summary>
93  /// Creates a new immutable array from the projected elements in the specified enumerable
94  /// </summary>
95  /// <typeparam name="T">The item type of the source enumerable</typeparam>
96  /// <typeparam name="TResult">The type of the items in the output array</typeparam>
97  /// <param name="enumerable">The items to be placed into the array</param>
98  /// <param name="selector">Selects items from the enumerable to be placed into the array</param>
99  /// <returns>A new array containing the items in the enumerable</returns>
100  public static ImmutableArray<TResult> ToImmutableArray<T, TResult>(this IEnumerable<T> enumerable, Func<T, TResult> selector)
101  {
102  return enumerable.Select(selector).ToImmutableArray();
103  }
104 
105  /// <summary>
106  /// Returns true if the specified enumerable is null or has no elements
107  /// </summary>
108  /// <typeparam name="T">The enumerable's item type</typeparam>
109  /// <param name="enumerable">The enumerable to check for a value</param>
110  /// <returns>True if the enumerable has elements, false otherwise</returns>
111  public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
112  {
113  return enumerable == null || !enumerable.Any();
114  }
115 
116  /// <summary>
117  /// Gets the median value in the collection
118  /// </summary>
119  /// <typeparam name="T">The item type in the collection</typeparam>
120  /// <param name="enumerable">The enumerable of items to search</param>
121  /// <returns>The median value, throws InvalidOperationException if no items are present</returns>
122  public static T Median<T>(this IEnumerable<T> enumerable)
123  {
124  var collection = enumerable.ToList();
125  return collection.OrderBy(x => x).Skip(collection.Count/2).First();
126  }
127 
128  /// <summary>
129  /// Gets the median value in the collection
130  /// </summary>
131  /// <typeparam name="T">The item type in the collection</typeparam>
132  /// <typeparam name="TProperty">The type of the value selected</typeparam>
133  /// <param name="collection">The collection of items to search</param>
134  /// <param name="selector">Function used to select a value from collection items</param>
135  /// <returns>The median value, throws InvalidOperationException if no items are present</returns>
136  public static TProperty Median<T, TProperty>(this IEnumerable<T> collection, Func<T, TProperty> selector)
137  {
138  return collection.Select(selector).Median();
139  }
140 
141  /// <summary>
142  /// Performs a binary search on the specified collection.
143  /// </summary>
144  /// <typeparam name="TItem">The type of the item.</typeparam>
145  /// <typeparam name="TSearch">The type of the searched item.</typeparam>
146  /// <param name="list">The list to be searched.</param>
147  /// <param name="value">The value to search for.</param>
148  /// <param name="comparer">The comparer that is used to compare the value with the list items.</param>
149  /// <returns>The index of the item if found, otherwise the bitwise complement where the value should be per MSDN specs</returns>
150  public static int BinarySearch<TItem, TSearch>(this IList<TItem> list, TSearch value, Func<TSearch, TItem, int> comparer)
151  {
152  if (list == null)
153  {
154  throw new ArgumentNullException(nameof(list));
155  }
156  if (comparer == null)
157  {
158  throw new ArgumentNullException(nameof(comparer));
159  }
160 
161  var lower = 0;
162  var upper = list.Count - 1;
163 
164  while (lower <= upper)
165  {
166  var middle = lower + (upper - lower) / 2;
167  var comparisonResult = comparer(value, list[middle]);
168  if (comparisonResult < 0)
169  {
170  upper = middle - 1;
171  }
172  else if (comparisonResult > 0)
173  {
174  lower = middle + 1;
175  }
176  else
177  {
178  return middle;
179  }
180  }
181 
182  return ~lower;
183  }
184 
185  /// <summary>
186  /// Performs a binary search on the specified collection.
187  /// </summary>
188  /// <typeparam name="TItem">The type of the item.</typeparam>
189  /// <param name="list">The list to be searched.</param>
190  /// <param name="value">The value to search for.</param>
191  /// <returns>The index of the item if found, otherwise the bitwise complement where the value should be per MSDN specs</returns>
192  public static int BinarySearch<TItem>(this IList<TItem> list, TItem value)
193  {
194  return BinarySearch(list, value, Comparer<TItem>.Default);
195  }
196 
197  /// <summary>
198  /// Performs a binary search on the specified collection.
199  /// </summary>
200  /// <typeparam name="TItem">The type of the item.</typeparam>
201  /// <param name="list">The list to be searched.</param>
202  /// <param name="value">The value to search for.</param>
203  /// <param name="comparer">The comparer that is used to compare the value with the list items.</param>
204  /// <returns>The index of the item if found, otherwise the bitwise complement where the value should be per MSDN specs</returns>
205  public static int BinarySearch<TItem>(this IList<TItem> list, TItem value, IComparer<TItem> comparer)
206  {
207  return list.BinarySearch(value, comparer.Compare);
208  }
209 
210  /// <summary>
211  /// Wraps the specified enumerable such that it will only be enumerated once
212  /// </summary>
213  /// <typeparam name="T">The enumerable's element type</typeparam>
214  /// <param name="enumerable">The source enumerable to be wrapped</param>
215  /// <returns>A new enumerable that can be enumerated multiple times without re-enumerating the source enumerable</returns>
216  public static IEnumerable<T> Memoize<T>(this IEnumerable<T> enumerable)
217  {
218  if (enumerable is MemoizingEnumerable<T>) return enumerable;
219  return new MemoizingEnumerable<T>(enumerable);
220  }
221 
222  /// <summary>
223  /// Produces the an enumerable of the range of values between start and end using the specified
224  /// incrementing function
225  /// </summary>
226  /// <typeparam name="T">The enumerable item type</typeparam>
227  /// <param name="start">The start of the range</param>
228  /// <param name="end">The end of the range, non-inclusive by default</param>
229  /// <param name="incrementer">The incrementing function, with argument of the current item</param>
230  /// <param name="includeEndPoint">True to emit the end point, false otherwise</param>
231  /// <returns>An enumerable of the range of items between start and end</returns>
232  public static IEnumerable<T> Range<T>(T start, T end, Func<T, T> incrementer, bool includeEndPoint = false)
233  where T : IComparable
234  {
235  var current = start;
236  if (includeEndPoint)
237  {
238  while (current.CompareTo(end) <= 0)
239  {
240  yield return current;
241  current = incrementer(current);
242  }
243  }
244  else
245  {
246  while (current.CompareTo(end) < 0)
247  {
248  yield return current;
249  current = incrementer(current);
250  }
251  }
252  }
253 
254  /// <summary>
255  /// Groups adjacent elements of the enumerale using the specified grouper function
256  /// </summary>
257  /// <typeparam name="T">The enumerable item type</typeparam>
258  /// <param name="enumerable">The source enumerable to be grouped</param>
259  /// <param name="grouper">A function that accepts the previous value and the next value and returns
260  /// true if the next value belongs in the same group as the previous value, otherwise returns false</param>
261  /// <returns>A new enumerable of the groups defined by grouper. These groups don't have a key
262  /// and are only grouped by being emitted separately from this enumerable</returns>
263  public static IEnumerable<IEnumerable<T>> GroupAdjacentBy<T>(this IEnumerable<T> enumerable, Func<T, T, bool> grouper)
264  {
265  using (var e = enumerable.GetEnumerator())
266  {
267  if (e.MoveNext())
268  {
269  var list = new List<T> {e.Current};
270  var pred = e.Current;
271  while (e.MoveNext())
272  {
273  if (grouper(pred, e.Current))
274  {
275  list.Add(e.Current);
276  }
277  else
278  {
279  yield return list;
280  list = new List<T> {e.Current};
281  }
282  pred = e.Current;
283  }
284  yield return list;
285  }
286  }
287  }
288 
289  /// <summary>
290  /// Determines if there are any differences between the left and right collections.
291  /// This method uses sets to improve performance and also uses lazy evaluation so if a
292  /// difference is found, true is immediately returned and evaluation is halted.
293  /// </summary>
294  /// <typeparam name="T">The item type</typeparam>
295  /// <param name="left">The left set</param>
296  /// <param name="right">The right set</param>
297  /// <returns>True if there are any differences between the two sets, false otherwise</returns>
298  public static bool AreDifferent<T>(this ISet<T> left, ISet<T> right)
299  {
300  if(ReferenceEquals(left, right))
301  {
302  return false;
303  }
304  return !left.SetEquals(right);
305  }
306 
307  /// <summary>
308  /// Converts an <see cref="IEnumerator{T}"/> to an <see cref="IEnumerable{T}"/>
309  /// </summary>
310  /// <typeparam name="T">Collection element type</typeparam>
311  /// <param name="enumerator">The enumerator to convert to an enumerable</param>
312  /// <returns>An enumerable wrapping the specified enumerator</returns>
313  public static IEnumerable<T> AsEnumerable<T>(this IEnumerator<T> enumerator)
314  {
315  using (enumerator)
316  {
317  while (enumerator.MoveNext())
318  {
319  yield return enumerator.Current;
320  }
321  }
322  }
323 
324  /// <summary>
325  /// Gets the value associated with the specified key or provided default value if key is not found.
326  /// </summary>
327  /// <typeparam name="K">The key type</typeparam>
328  /// <typeparam name="V">The value type</typeparam>
329  /// <param name="dictionary">The dictionary instance</param>
330  /// <param name="key">Lookup key</param>
331  /// <param name="defaultValue">Default value</param>
332  /// <returns>Value associated with the specified key or default value</returns>
333  public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dictionary, K key, V defaultValue = default(V))
334  {
335  V obj;
336  return dictionary.TryGetValue(key, out obj) ? obj : defaultValue;
337  }
338 
339  /// <summary>
340  /// Performs an action for each element in collection source
341  /// </summary>
342  /// <typeparam name="T"></typeparam>
343  /// <param name="source">Collection source</param>
344  /// <param name="action">An action to perform</param>
345  public static void DoForEach<T>(this IEnumerable<T> source, Action<T> action)
346  {
347  foreach (var element in source)
348  {
349  action(element);
350  }
351  }
352  }
353 }