Closed
Description
I would like to suggest adding ScanBy
. The idea has floated out of review of PR #562 that proposes to add IndexBy
(see also #560) but where ScanBy
would be an even more general implementation.
Prototype
public static IEnumerable<TResult>
ScanBy<T, TKey, TState, TResult>(
this IEnumerable<T> source,
Func<T, TKey> keySelector,
Func<TKey, TState> seedSelector,
Func<T, TKey, TState, TState> accumulator,
Func<T, TKey, TState, TResult> resultSelector,
IEqualityComparer<TKey> comparer = null)
{
var stateByKey = new Dictionary<TKey, TState>(comparer);
foreach (var item in source)
{
var key = keySelector(item);
if (!stateByKey.TryGetValue(key, out var state))
state = seedSelector(key);
state = accumulator(item, key, state);
stateByKey[key] = state;
yield return resultSelector(item, key, state);
}
}
Example
As an example, here is IndexBy
implemented in terms of ScanBy
:
public static IEnumerable<TResult>
IndexBy<TSource, TKey, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, TKey, int, TResult> resultSelector,
IEqualityComparer<TKey> comparer) =>
source.ScanBy(keySelector, k => -1, (e, k, i) => i + 1, resultSelector, comparer);
Do we need such a function? Not really, because it could be implemented ad-hoc:
foreach (var (x, k, i) in
Enumerable.Range(0, 20)
.Shuffle()
.ScanBy(x => x / 5, k => -1, (x, k, i) => i + 1, ValueTuple.Create))
{
// do something...
}
Here's a running max by key/group:
foreach (var (x, k, max) in
Enumerable.Range(0, 20)
.Shuffle()
.ScanBy(x => x / 5,
k => int.MinValue,
(x, k, m) => Math.Max(x, m),
ValueTuple.Create);
{
// do something...
}