Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compute a running sum of a series of ints in a Linq query?

Tags:

c#

linq

I am trying to come up with a linq query to convert an IEnumerable<int> to another IEnumerable<int>, where each int in the result is the sum of all the ints up to that position from the initial 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] and so on

Alternatively, the sums above can be written as b[1] = b[0] + a[1], b[2] = b[1] + a[2] and so on, but I don't see how that would help.

I can, of course, do this with a for loop, but I obtain the a[] sequence from a query and I thought it would look nicer if I continue that query instead of suddenly adding a for there :)

like image 414
Marcel Popescu Avatar asked Jul 26 '09 15:07

Marcel Popescu


People also ask

How do you find the sum of Linq?

In LINQ, you can find the sum of the given numeric elements by using the Sum() method. This method calculates the sum of the numeric value present in the given sequence. It does not support query syntax in C#, but it supports in VB.NET. It is available in both Enumerable and Queryable classes in C#.

How do you sum a column in LINQ?

Aggregate FunctionSUM() : Returns the sum of column values. AVERAGE() : Returns the average of column values. COUNT() : Returns the total number of rows in a table. MAX() : Returns the maximum value in the column.

What is any () in Linq?

The Any operator is used to check whether any element in the sequence or collection satisfy the given condition. If one or more element satisfies the given condition, then it will return true. If any element does not satisfy the given condition, then it will return false.


3 Answers

Well, you can do it with side effects easily enough, although it's pretty icky...

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

It would be nice if the framework provided a sort of "running aggregate" to encapsulate this, but it doesn't as far as I'm aware.

like image 53
Jon Skeet Avatar answered Oct 20 '22 15:10

Jon Skeet


I wrote a function to do this a while ago. It's similar to Haskell's 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();
like image 8
Cameron MacFarland Avatar answered Oct 20 '22 15:10

Cameron MacFarland


An alternative to Mr. Skeet's solution: If we drop the requirement for a linq query and more literally address "convert an 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 don't want to create the whole array at once.

like image 5
Curt Nichols Avatar answered Oct 20 '22 16:10

Curt Nichols