Lean  $LEAN_TAG$
BinaryComparisonExtensions.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.Linq.Expressions;
20 
21 namespace QuantConnect
22 {
23  /// <summary>
24  /// Provides convenience extension methods for applying a <see cref="BinaryComparison"/> to collections.
25  /// </summary>
26  public static class BinaryComparisonExtensions
27  {
28  /// <summary>
29  /// Filters the provided <paramref name="values"/> according to this <see cref="BinaryComparison"/>
30  /// and the specified <paramref name="reference"/> value. The <paramref name="reference"/> value is
31  /// used as the RIGHT side of the binary comparison. Consider the binary comparison is LessThan and
32  /// we call Filter(values, 42). We're looking for keys that are less than 42.
33  /// </summary>
34  public static TCollection Filter<T, TCollection>(
35  this BinaryComparison comparison,
36  TCollection values,
37  T reference
38  )
39  where TCollection : ICollection<T>, new()
40  {
41  var result = new TCollection();
42  var evaluator = comparison.GetEvaluator<T>();
43  foreach (var value in values)
44  {
45  if (evaluator(value, reference))
46  {
47  result.Add(value);
48  }
49  }
50 
51  return result;
52  }
53 
54  /// <summary>
55  /// Filters the provided <paramref name="values"/> according to this <see cref="BinaryComparison"/>
56  /// and the specified <paramref name="reference"/> value. The <paramref name="reference"/> value is
57  /// used as the RIGHT side of the binary comparison. Consider the binary comparison is LessThan and
58  /// we call Filter(values, 42). We're looking for keys that are less than 42.
59  /// </summary>
60  public static SortedDictionary<TKey, TValue> Filter<TKey, TValue>(
61  this BinaryComparison comparison,
62  SortedDictionary<TKey, TValue> values,
63  TKey reference
64  )
65  {
66  SortedDictionary<TKey, TValue> result;
67  if (comparison.Type == ExpressionType.NotEqual)
68  {
69  result = new SortedDictionary<TKey, TValue>(values);
70  result.Remove(reference);
71  return result;
72  }
73 
74  result = new SortedDictionary<TKey, TValue>();
75  if (comparison.Type == ExpressionType.Equal)
76  {
77  TValue value;
78  if (values.TryGetValue(reference, out value))
79  {
80  result.Add(reference, value);
81  }
82 
83  return result;
84  }
85 
86  // since we're enumerating a sorted collection, once we receive
87  // a mismatch it means we'll never again receive a match
88  var breakAfterFailure =
89  comparison == BinaryComparison.LessThanOrEqual ||
90  comparison == BinaryComparison.LessThanOrEqual;
91 
92  var evaluator = comparison.GetEvaluator<TKey>();
93  foreach (var kvp in values)
94  {
95  if (evaluator(kvp.Key, reference))
96  {
97  result.Add(kvp.Key, kvp.Value);
98  }
99  else if (breakAfterFailure)
100  {
101  break;
102  }
103  }
104 
105  return result;
106  }
107 
108  /// <summary>
109  /// Filters the provided <paramref name="values"/> according to this <see cref="BinaryComparison"/>
110  /// and the specified <paramref name="reference"/> value. The <paramref name="reference"/> value is
111  /// used as the RIGHT side of the binary comparison. Consider the binary comparison is LessThan and
112  /// we call Filter(values, 42). We're looking for keys that are less than 42.
113  /// </summary>
114  public static ImmutableSortedDictionary<TKey, TValue> Filter<TKey, TValue>(
115  this BinaryComparison comparison,
116  ImmutableSortedDictionary<TKey, TValue> values,
117  TKey reference
118  )
119  {
120  if (comparison.Type == ExpressionType.NotEqual)
121  {
122  return values.Remove(reference);
123  }
124 
125  var result = ImmutableSortedDictionary<TKey, TValue>.Empty;
126  if (comparison.Type == ExpressionType.Equal)
127  {
128  TValue value;
129  if (values.TryGetValue(reference, out value))
130  {
131  result = result.Add(reference, value);
132  }
133 
134  return result;
135  }
136 
137  // since we're enumerating a sorted collection, once we receive
138  // a mismatch it means we'll never again receive a match
139  var breakAfterFailure =
140  comparison == BinaryComparison.LessThanOrEqual ||
141  comparison == BinaryComparison.LessThanOrEqual;
142 
143  var evaluator = comparison.GetEvaluator<TKey>();
144  foreach (var kvp in values)
145  {
146  if (evaluator(kvp.Key, reference))
147  {
148  result = result.Add(kvp.Key, kvp.Value);
149  }
150  else if (breakAfterFailure)
151  {
152  break;
153  }
154  }
155 
156  return result;
157  }
158 
159  /// <summary>
160  /// Filters the provided <paramref name="values"/> according to this <see cref="BinaryComparison"/>
161  /// and the specified <paramref name="reference"/> value. The <paramref name="reference"/> value is
162  /// used as the RIGHT side of the binary comparison. Consider the binary comparison is LessThan and
163  /// we call Filter(values, 42). We're looking for keys that are less than 42.
164  /// </summary>
165  public static Tuple<ImmutableSortedDictionary<TKey, TValue>, ImmutableSortedDictionary<TKey, TValue>> SplitBy<TKey, TValue>(
166  this BinaryComparison comparison,
167  ImmutableSortedDictionary<TKey, TValue> values,
168  TKey reference
169  )
170  {
171  var matches = ImmutableSortedDictionary<TKey, TValue>.Empty;
172  var removed = ImmutableSortedDictionary<TKey, TValue>.Empty;
173 
174  if (comparison.Type == ExpressionType.NotEqual)
175  {
176  var match = values.Remove(reference);
177  removed = BinaryComparison.Equal.Filter(values, reference);
178  return Tuple.Create(match, removed);
179  }
180 
181  if (comparison.Type == ExpressionType.Equal)
182  {
183  TValue value;
184  if (values.TryGetValue(reference, out value))
185  {
186  matches = matches.Add(reference, value);
187  removed = BinaryComparison.NotEqual.Filter(values, reference);
188  return Tuple.Create(matches, removed);
189  }
190 
191  // no matches
192  return Tuple.Create(ImmutableSortedDictionary<TKey, TValue>.Empty, values);
193  }
194 
195  var evaluator = comparison.GetEvaluator<TKey>();
196  foreach (var kvp in values)
197  {
198  if (evaluator(kvp.Key, reference))
199  {
200  matches = matches.Add(kvp.Key, kvp.Value);
201  }
202  else
203  {
204  removed = removed.Add(kvp.Key, kvp.Value);
205  }
206  }
207 
208  return Tuple.Create(matches, removed);
209  }
210  }
211 }