Summing the previous values in an IEnumerable

前端 未结 7 2299
有刺的猬
有刺的猬 2021-01-04 02:36

I have a sequence of numbers:

var seq = new List { 1, 3, 12, 19, 33 };

and I want to transform that into a new sequence where

相关标签:
7条回答
  • 2021-01-04 02:45

    "Pure" LINQ:

    var result = seq.Select((a, i) => seq.Take(i + 1).Sum());

    One more "pure" LINQ O(n):

    var res = Enumerable.Range(0, seq.Count)
        .Select(a => a == 0 ? seq[a] : seq[a] += seq[a - 1]);
    

    One more LINQ, with state maintenance:

    var tmp = 0;
    var result = les.Select(a => { tmp += a; return tmp; });
    
    0 讨论(0)
  • 2021-01-04 02:47

    Just to offer another alternative, albeit not really LINQ, you could write a yield-based function to do the aggregation:

    public static IEnumerable<int> SumSoFar(this IEnumerable<int> values)
    {
      int sumSoFar = 0;
      foreach (int value in values)
      {
        sumSoFar += value;
        yield return sumSoFar;
      }
    }
    

    Like BrokenGlass's this makes only a single pass over the data although unlike his returns an iterator not a list.

    (Annoyingly you can't easily make this generic on the numeric type in the list.)

    0 讨论(0)
  • 2021-01-04 02:47
    var seq = new List<int> { 1, 3, 12, 19, 33 }; 
    
    for (int i = 1; i < seq.Count; i++)
    {
       seq[i] += seq[i-1];
    }
    
    0 讨论(0)
  • 2021-01-04 02:59
    var seq = new List<int> { 1, 3, 12, 19, 33 };
    
    var summed = new List<int>();
    
    seq.ForEach(i => summed.Add(i + summed.LastOrDefault()));
    
    0 讨论(0)
  • 2021-01-04 03:00

    To use Linq and only iterate over the list once you could use a custom aggregator:

    class Aggregator
    {
        public List<int> List { get; set; }
        public int Sum { get; set; }
    }
    

    ..

    var seq = new List<int> { 1, 3, 12, 19, 33 };
    var aggregator = new Aggregator{ List = new List<int>(), Sum = 0 };
    var aggregatorResult = seq.Aggregate(aggregator, (a, number) => { a.Sum += number; a.List.Add(a.Sum); return a; });
    var result = aggregatorResult.List;
    
    0 讨论(0)
  • 2021-01-04 03:02

    Stephen Swensen's answer is great, scan is exactly what you need. There is another version of scan though that doesn't require a seed, which would be slightly more appropriate for your exact problem.

    This version requires that your output element type is the same as your input element type, which it is in your case, and gives the advantage of not needing you to pass in a 0 and then Skip the first (0) result.

    You can implement this version of scan in C# as follows:

    public static IEnumerable<T> Scan<T>(this IEnumerable<T> Input, Func<T, T, T> Accumulator)
    {
        using (IEnumerator<T> enumerator = Input.GetEnumerator())
        {
            if (!enumerator.MoveNext())
                yield break;
            T state = enumerator.Current;
            yield return state;
            while (enumerator.MoveNext())
            {
                state = Accumulator(state, enumerator.Current);
                yield return state;
            }
        }
    }
    

    And then use it as follows:

    IEnumerable<int> seq = new List<int> { 1, 3, 12, 19, 33 };
    IEnumerable<int> transformed = seq.Scan((state, item) => state + item);
    
    0 讨论(0)
提交回复
热议问题