-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
AggregateBy.cs
174 lines (166 loc) · 6.42 KB
/
AggregateBy.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
namespace SuperLinq;
public static partial class SuperEnumerable
{
/// <summary>
/// Applies a key-generating function to each element of a sequence and returns an aggregate value for each key.
/// An additional argument specifies a comparer to use for testing equivalence of keys.
/// </summary>
/// <typeparam name="TSource">
/// Type of the elements of the source sequence.
/// </typeparam>
/// <typeparam name="TKey">
/// Type of the projected element.
/// </typeparam>
/// <typeparam name="TAccumulate">
/// Type of the accumulator value.
/// </typeparam>
/// <param name="source">
/// Source sequence.
/// </param>
/// <param name="keySelector">
/// Function that transforms each item of source sequence into a key to be compared against the others.
/// </param>
/// <param name="seed">
/// The initial accumulator value for each key-group.
/// </param>
/// <param name="func">
/// An accumulator function to be invoked on each element. The accumulator value is tracked separately for each
/// key.
/// </param>
/// <param name="comparer">
/// The equality comparer to use to determine whether or not keys are equal. If <see langword="null" />, the
/// default equality comparer for <typeparamref name="TKey"/> is used.
/// </param>
/// <returns>
/// A sequence of unique keys and their accumulated value.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="source"/>, <paramref name="keySelector"/>, or <paramref name="func"/> is <see
/// langword="null" />.
/// </exception>
/// <remarks>
/// <para>
/// This method is implemented by using deferred execution. The operator will be executed in it's entirety
/// immediately when the sequence is first enumerated.
/// </para>
/// </remarks>
#if NET9_0_OR_GREATER
[Obsolete("This method has been implemented by the framework.")]
public static IEnumerable<KeyValuePair<TKey, TAccumulate>> AggregateBy<TSource, TKey, TAccumulate>(
IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> func,
IEqualityComparer<TKey>? comparer = null) where TKey : notnull
#else
public static IEnumerable<KeyValuePair<TKey, TAccumulate>> AggregateBy<TSource, TKey, TAccumulate>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> func,
IEqualityComparer<TKey>? comparer = null) where TKey : notnull
#endif
{
return AggregateBy(source, keySelector, _ => seed, func, comparer);
}
/// <summary>
/// Applies a key-generating function to each element of a sequence and returns an aggregate value for each key.
/// An additional argument specifies a comparer to use for testing equivalence of keys.
/// </summary>
/// <typeparam name="TSource">
/// Type of the elements of the source sequence.
/// </typeparam>
/// <typeparam name="TKey">
/// Type of the projected element.
/// </typeparam>
/// <typeparam name="TAccumulate">
/// Type of the accumulator value.
/// </typeparam>
/// <param name="source">
/// Source sequence.
/// </param>
/// <param name="keySelector">
/// Function that transforms each item of source sequence into a key to be compared against the others.
/// </param>
/// <param name="seedSelector">
/// A function that returns the initial seed for each key.
/// </param>
/// <param name="func">
/// An accumulator function to be invoked on each element. The accumulator value is tracked separately for each
/// key.
/// </param>
/// <param name="comparer">
/// The equality comparer to use to determine whether or not keys are equal. If <see langword="null" />, the
/// default equality comparer for <typeparamref name="TKey"/> is used.
/// </param>
/// <returns>
/// A sequence of unique keys and their accumulated value.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="source"/>, <paramref name="seedSelector"/>, <paramref name="keySelector"/>, or <paramref
/// name="func"/> is <see langword="null" />.
/// </exception>
/// <remarks>
/// <para>
/// This method is implemented by using deferred execution. The operator will be executed in it's entirety
/// immediately when the sequence is first enumerated.
/// </para>
/// </remarks>
#if NET9_0_OR_GREATER
[Obsolete("This method has been implemented by the framework.")]
public static IEnumerable<KeyValuePair<TKey, TAccumulate>> AggregateBy<TSource, TKey, TAccumulate>(
IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TKey, TAccumulate> seedSelector,
Func<TAccumulate, TSource, TAccumulate> func,
IEqualityComparer<TKey>? comparer = null) where TKey : notnull
#else
public static IEnumerable<KeyValuePair<TKey, TAccumulate>> AggregateBy<TSource, TKey, TAccumulate>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TKey, TAccumulate> seedSelector,
Func<TAccumulate, TSource, TAccumulate> func,
IEqualityComparer<TKey>? comparer = null) where TKey : notnull
#endif
{
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(keySelector);
ArgumentNullException.ThrowIfNull(seedSelector);
ArgumentNullException.ThrowIfNull(func);
return Core(source, keySelector, seedSelector, func, comparer ?? EqualityComparer<TKey>.Default);
static IEnumerable<KeyValuePair<TKey, TAccumulate>> Core(
IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TKey, TAccumulate> seedSelector,
Func<TAccumulate, TSource, TAccumulate> func,
IEqualityComparer<TKey> comparer)
{
foreach (var kvp in Loop(source, keySelector, seedSelector, func, comparer))
yield return kvp;
}
static Dictionary<TKey, TAccumulate> Loop(
IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TKey, TAccumulate> seedSelector,
Func<TAccumulate, TSource, TAccumulate> func,
IEqualityComparer<TKey> cmp)
{
var dict = new Dictionary<TKey, TAccumulate>(cmp);
foreach (var item in source)
{
var key = keySelector(item);
#if NET5_0_OR_GREATER
ref var acc = ref System.Runtime.InteropServices.CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out var exists);
acc = func(exists ? acc! : seedSelector(key), item);
#else
dict[key] = func(
dict.TryGetValue(key, out var acc)
? acc
: seedSelector(key),
item);
#endif
}
return dict;
}
}
}