Home > Enterprise >  LINQ equivalent to Seq.scan
LINQ equivalent to Seq.scan

Time:08-24

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);
  • Related