Description
I'd like to propose adding an overload of Aggregate
that will use a function to determine the validity of each item in the source sequence. As long as items are valid, they will be accumulated and a function will be called at the end to turn the accumulator into a result. As soon as one item is invalid, the iteration of the source sequence will be halted and a function will be called to turn the partial accumulation into a result.
This enables one to implement the following strategies:
- Accumulate all good items or none.
- Accumulate all good items or the good ones so far (partial/some case).
A prototype of such an extension would be as follows:
static partial class MoreEnumerable
{
public static TResult
Aggregate<TFirst, TState, TSecond, TResult>(
this IEnumerable<TFirst> source,
TState seed,
Func<TFirst, (bool, TSecond)> secondChooser,
Func<TState, TSecond, TState> folder,
Func<TState, TResult> allSelector,
Func<TState, TFirst, TResult> partialSelector)
{
var state = seed;
foreach (var first in source)
{
if (secondChooser(first) is (true, var second))
state = folder(state, second);
else
return partialSelector(state, first);
}
return allSelector(state);
}
}
This is like Choose
+ Aggregate
rolled into one, but which cannot be done otherwise by combining the two without considerable and additional effort. The following example shows the above in action, together with how it differs from Choose
:
using System;
using System.Collections.Immutable;
using MoreLinq;
var inputs =
from s in new[]
{
"O,l,2,3,4,S,6,7,B,9",
"0,1,2,3,4,5,6,7,8,9",
}
select new
{
Source = s,
Tokens = s.Split(','),
};
foreach (var input in inputs)
{
var xs = input.Tokens.Choose(s => (int.TryParse(s, out var n), n));
Console.WriteLine($"The good stuff: {string.Join(", ", xs)}");
if (input.Tokens
.Aggregate(ImmutableArray<int>.Empty,
s => (int.TryParse(s, out var n), n),
(a, n) => a.Add(n),
a => a,
(_, _) => default) is { IsDefault: false } ys)
{
Console.WriteLine($"All okay: {string.Join(", ", ys)}");
}
else
{
Console.WriteLine($"Input is bad: {input.Source}");
}
}
The output of the above example will be as follows:
The good stuff: 2, 3, 4, 6, 7, 9
Input is bad: O,l,2,3,4,S,6,7,B,9
The good stuff: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
All okay: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9