How to calculate the current sum of an int series in a Linq query?

I am trying to come up with a linq query to convert an IEnumerable<int> to another IEnumerable<int> , where each int as a result is the sum of all ints to this position from the original list:

Given int[] a
I need int[] b
Where b[0] = a[0], b[1] = a[0] + a[1], b[2] = a[0] + a[1] + a[2] , etc.

Alternatively, the amounts above can be written as b[1] = b[0] + a[1], b[2] = b[1] + a[2] etc., but I don’t see how that might help.

I can, of course, do this with a for loop, but I get the sequence [] from the request, and I thought it would look better if I continued this request instead of suddenly adding for there :)

+5
c # linq
Jul 26 '09 at 15:14
source share
4 answers

Well, you can do it with side effects quite easily, although it's pretty bad ...

 int sum = 0; int[] b = a.Select(x => (sum += x)).ToArray(); 

It would be nice if the infrastructure provided a kind of “working aggregate” to encapsulate this, but this is not as far as I know.

+13
Jul 26 '09 at 15:18
source share

I wrote a function to do this a while ago. It is similar to the Haskell scanl function.

 public static IEnumerable<TResult> Scan<T, TResult>( this IEnumerable<T> source, Func<T, T, TResult> combine) { using (IEnumerator<T> data = source.GetEnumerator()) if (data.MoveNext()) { T first = data.Current; yield return first; while (data.MoveNext()) { first = combine(first, data.Current); yield return first; } } } int[] b = a .Scan((running, current) => running + current) .ToArray(); 
+8
Jul 26 '09 at 15:41
source share

An alternative to Mr. Skeet's solution: if we refuse the linq query and more literally refer to "convert IEnumerable<int> to another IEnumerable<int> ", we can use this:

  static IEnumerable<int> Sum(IEnumerable<int> a) { int sum = 0; foreach (int i in a) { sum += i; yield return sum; } } 

which we can apply to an infinite series:

  foreach (int i in Sum(MyMath.NaturalNumbers)) Console.WriteLine(i); 

This is also useful if you do not want to instantly create the entire array.

+5
Jul 26 '09 at 16:29
source share

The above answer does not quite work .... and does not have the same signature as Haskell scanl .... basically it is an extension of the linq "aggregate" idea ... I think this is better suited to the Haskell implementation

  public static IEnumerable<TResult> Scanl<T, TResult>( this IEnumerable<T> source, TResult first, Func<TResult, T, TResult> combine) { using (IEnumerator<T> data = source.GetEnumerator()) { yield return first; while (data.MoveNext()) { first = combine(first, data.Current); yield return first; } } } 

Using

  [TestMethod] public void Scanl_Test() { var xs = new int[] { 1, 2, 3, 4, 5, 6, 7 }; var lazyYs = xs.Scanl(0, (y, x) => y + x); var ys = lazyYs.ToArray(); Assert.AreEqual(ys[0], 0); Assert.AreEqual(ys[1], 1); Assert.AreEqual(ys[2], 3); Assert.AreEqual(ys[3], 6); Assert.AreEqual(ys[4], 10); Assert.AreEqual(ys[5], 15); Assert.AreEqual(ys[6], 21); Assert.AreEqual(ys[7], 28); } 
0
May 09 '13 at 10:08
source share



All Articles