-
Notifications
You must be signed in to change notification settings - Fork 419
Description
I propose to add an extension that takes one or more elements at the head of a sequence and spills a projection to remaining elements of the sequence.
Sometimes you have a sequence where the initial element(s) contains information about the processing of the rest of the sequence. A typical example is a table (think CSV) where the table is composed of rows; where the first row is a header and the remaining the data rows. Processing such a sequence should only generate a projection of the data rows.
The signature would be as follows:
public static IEnumerable<R>
SpillSpan<T, M, A, H, R>(
this IEnumerable<T> source,
Func<T, (bool, M)> chooser,
A empty,
Func<M, A> seeder,
Func<A, M, A> accumulator,
Func<A, H> headerSelector,
Func<H, T, R> resultSelector)
The chooser
identifies header elements. Data rows commence as soon as it returns (false, _)
for a T
. The header elements are accumulated via accumulator
and the seeder
is used to seed the accumulation with the initial header element. The empty
value is used for the headless case. The headerSelector
function is used to create a single projection out of the accumulated header elements and which is subsequently paired with or spilled to remaining elements.
I propose to add overloads for simpler cases.
SpillSpan
should never throw an exception. If the user wants to ban the headless case, he/she can throw in headerSelector
upon receiving the empty
value.
Example
const string csv = @"
a,c,b
1,3,2
4,6,5
7,9,8";
data result =
from rows in new[]
{
from line in Regex.Split(csv.Trim(), @"\r?\n")
select line.Split(',').Select(f => f.Trim()).ToArray()
}
from row in
rows.Index()
.SpillSpan(
r => r.Key == 0,
null,
r => r.Value,
(r, _) => r,
h => MoreEnumerable.Return(h.Index().ToDictionary(e => e.Value, e => e.Key)) // name => index
.SelectMany(_ => new[] { "a", "b", "c" }, (m, n) => m[n]) // lookup index of name
.ToArray(),
(bs, r) => bs.Select(i => int.Parse(r.Value[i], CultureInfo.InvariantCulture))
.Fold((a, b, c) => new { A = a, B = b, C = c }))
select row;
foreach (var row in data)
Console.WriteLine(row);
Output:
{ A = 1, B = 2, C = 3 }
{ A = 4, B = 5, C = 6 }
{ A = 7, B = 8, C = 9 }