Does C# have a LINQ equivalent to F#'s Seq.scan
--like fold
but storing the aggregated value at every step? (One like reduce
that doesn't require a starting state would work at least as well.)
If one does not exist, I can work around its absence by writing my own or using LanguageExt's method, but I prefer to use core libraries when possible.
CodePudding user response:
C# does not have a direct LINQ equivalent to F#'s Seq.scan
method.
You can mimic this functionality with the LINQ Aggregator method by passing a collection as the seed value and performing lookups on the previous accumulations:
public class Data
{
public int Count { get; set; }
}
var data = new List<Data>
{
new Data { Count = 1 },
new Data { Count = 2 },
new Data { Count = 1 },
new Data { Count = 5 },
new Data { Count = 3 }
};
var accumulatedValues = data.Aggregate(new List<int>(), (current, nextValue) =>
{
var lastValue = current.Count > 0 ? current[current.Count - 1] : 0;
var updatedValue = lastValue nextValue.Count;
current.Add(updatedValue);
return current;
});
In addition, it is relatively trivial to create your own extension method that mimics the Aggregate
function, but returns a list of all accumulation steps instead of a single value.
public static class Extensions
{
public static IEnumerable<TAccumulate> Accumulate<TSource, TAccumulate>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> accumulator)
{
var accumulation = new List<TAccumulate>();
var current = seed;
foreach (var item in source)
{
current = accumulator(current, item);
accumulation.Add(current);
}
return accumulation;
}
}
This works well for both collections of primitive values as well as collections of objects:
var firstAccumulation =
Enumerable.Range(1, 10).Accumulate(0, (acc, newValue) => acc newValue);
or
public class Data
{
public int Count { get; set; }
}
var data = new List<Data>
{
new Data { Count = 1 },
new Data { Count = 2 },
new Data { Count = 1 },
new Data { Count = 5 },
new Data { Count = 3 }
};
var secondAccumulation = data.Accumulate(0, (acc, newValue) => acc newValue.Count);