1

I have this scenario where I have to add the numbers inside a collection. An example would serve a best example.

I have these values in my database:

| Foo |
| 1   |
| 5   |
| 8   |
| 4   |

Result:

| Foo | Result |
|  1  | 1      |
|  5  | 6      |
|  8  | 14     |
|  4  | 18     |

As you can see, it has a somewhat fibonacci effect but the twist here is the numbers are given.

I can achieve this result with the help of for loop but is this possible doing in Linq. Like querying the database then having a result like above?

Any help would be much appreciated. Thanks!

Boy Pasmo
  • 8,021
  • 13
  • 42
  • 67
  • So you want running totals, is this a collection in memory or a database query? – Tim Schmelter Apr 02 '15 at 13:42
  • Yes. Running totals. Thank you for the term. Having hard time what to call it. To answer your question, database query. I would like to query this using Linq. – Boy Pasmo Apr 02 '15 at 13:44
  • Does your data base is SQL ? Also, (@Servy), is it possible to achieve this (server side computation of the running totals) with a SQL query ? If no, your question will have a response. – Orace Apr 02 '15 at 14:37
  • Yes. my DB is SQL. But with the one you posted as one of the answers below. Can I declare the `acc` variable inside the Linq? Well, the answer would be YES. But the resulting query would be different tho. – Boy Pasmo Apr 02 '15 at 14:40
  • @BoyPasmo None of the answers provided would be able to be translted into SQL by a query provider. I'm reasonably confident that it would be impossible to do with any LINQ provider. If it could be done in SQL at all, it'd need to be done with a stored proc that uses cursor's and such. – Servy Apr 02 '15 at 14:45
  • Thank you so much for your time. What should I do with my question tho? Should I marked the answer with almost-like solution? What would be your suggestion? For other's reference as well. – Boy Pasmo Apr 02 '15 at 14:48
  • @BoyPasmo That's your decision to make. – Servy Apr 02 '15 at 14:52

5 Answers5

4

I'm not sure how exactly you're touching the database, but here's a solution that can probably be improved upon:

var numbers = new List<int> { 1, 5, 8, 4 };
var result = numbers.Select((n, i) => numbers.Where((nn, ii) => ii <= i).Sum());

This overload of Select and Where takes the object (each number) and the index of that object. For each index, I used numbers.Where to Sum all the items with a lower and equal index.

For example, when the Select gets to the number 8 (index 2), numbers.Where grabs items with index 0-2 and sums them.

Jonesopolis
  • 25,034
  • 12
  • 68
  • 112
  • Hi. Thanks. This would be a stepping stone. But, think you can help me understand what you did? You didn't use the `n` parameter? – Boy Pasmo Apr 02 '15 at 13:50
  • Sure I added some info – Jonesopolis Apr 02 '15 at 13:52
  • I think using [`.Take`](https://msdn.microsoft.com/en-us/library/vstudio/bb503062%28v=vs.100%29.aspx) would be a bit cleaner (instead of `.Where (index stuff)` – ryanyuyu Apr 02 '15 at 13:54
  • 2
    Problem: you interrogate the data base O(n²) times. – Orace Apr 02 '15 at 13:54
  • Right. I'm not a LINQ expert, I assumed someone would improve my efficiency, like i noted. – Jonesopolis Apr 02 '15 at 13:55
  • @Orace Actually if this is EF or Linq-To-SQL it should create only one SQL query. It does run in O(n²) for Ling-To-Objects though. – juharr Apr 02 '15 at 14:06
  • more over if your array length is odd you will make one more iteration for doing the same thing of iteration-1 – BRAHIM Kamel Apr 02 '15 at 14:10
  • 1
    @juharr This overload of `Where` isn't supported in EF; it'd just crash. And in LINQ to objects is still *really* terribly implemented. It's constantly re-computing the same values over and over again. – Servy Apr 02 '15 at 14:12
  • @Servy didn't realize it wasn't supported, but I definitely knew it wouldn't call the DB n² times. – juharr Apr 02 '15 at 14:14
  • 2
    @juharr If `numbers` was an `IQueryable` that was cast to an `IEnumerable` then it *would* query the database n^2 times. If someone isn't careful when trying to implement this solution (or doesn't understand some of the intricacies of LINQ) they could very well end up doing that. – Servy Apr 02 '15 at 14:22
3

I think you can achieve this with :

var acc = 0;
var result = numbers.Select(i =>
    {
        acc += i;
        return acc;
    }).ToList();

You need the ToList to be sure it will be run only once (otherwise the acc will keep growing).

Also I'm not sure it can be converted to a query (and performed server side).

Thomas Levesque posted a response to a similar question where he provide a SelectAggregate method who provide the intermediate values of an aggregate computation.

It's look like this feature is not present in Linq by default, so you probably will not be able to perform the computation server side using Linq.

Community
  • 1
  • 1
Orace
  • 7,822
  • 30
  • 45
3

MoreLINQ has a Scan method that allows you to aggregate the values in a sequence while yielding each intermediate value, rather than just the final value, which is exactly what you're trying to do.

With that you can write:

var query = data.Scan((sum, next) => sum + next);

The one overload that you need here will be copied below. See the link above for details and additional overloads:

    public static IEnumerable<TSource> Scan<TSource>(this IEnumerable<TSource> source,
        Func<TSource, TSource, TSource> transformation)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (transformation == null) throw new ArgumentNullException("transformation");
        return ScanImpl(source, transformation);
    }

    private static IEnumerable<T> ScanImpl<T>(IEnumerable<T> source, Func<T, T, T> f)
    {
        using (var i = source.GetEnumerator())
        {
            if (!i.MoveNext())
                throw new InvalidOperationException("Sequence contains no elements.");

            var aggregator = i.Current;

            while (i.MoveNext())
            {
                yield return aggregator;
                aggregator = f(aggregator, i.Current);
            }
            yield return aggregator;
        }
    }
Servy
  • 202,030
  • 26
  • 332
  • 449
  • Of course this can't be translated into a db query either, but it will smoothly do the processing in-memory on results obtained from an IQueryable (at least, with Entity Framework and LINQ-to-SQL it will). So I think this is the best the OP can get (esp. when compared to some of the other answers). – Gert Arnold Apr 02 '15 at 14:40
  • Why it throws on empty source ? can't it just return an empty IEnumerable ? That is a stange behavior... – Orace Apr 04 '15 at 07:02
  • @Orace That's a decision decision, and both are justifiable positions. As I didn't write it, I can't explain why they choose to go with this implementation. You're of course free to adapt it when using it yourself (I would probably make that change myself). – Servy Apr 06 '15 at 13:59
2

You could do the following

int runningTotal = 0;
var runningTotals = numbers.Select(n => new 
    { 
        Number = n, 
        RunningTotal = (runningTotal += n)
    });

This will give you the number and the running total.

juharr
  • 31,741
  • 4
  • 58
  • 93
2

It's just an adaption of @Jonesy's answer:

int[] ints = new[] {1, 5, 8, 4};
var result = ints.Select((x, y) => x + ints.Take(y).Sum());
stefankmitph
  • 3,236
  • 2
  • 18
  • 22
  • And it has all of the same problems as that answer, namely it cannot be translated into a DB query, and is *super* inefficient as its constantly re-computing the same values over and over. – Servy Apr 02 '15 at 14:25
  • @Servy right, but the question was not: can this be made super effecient. it was: can it be done with linq. – stefankmitph Apr 02 '15 at 14:28
  • @Orace He's adding `x`, so no, it's not. If he removed that, then that would be the case. – Servy Apr 02 '15 at 14:30
  • @stefankmitph So you just don't *care* that this is a terrible implementation that should most certainly not be used for any reason? Sorry, but one does not need to ask in the question for an answer given to not be just *awful* in its implementation of the problem. – Servy Apr 02 '15 at 14:33
  • @Servy ok, it's terrible... fair enough, mate. but it's working... but what should i do? should i delete my answer? if so, tell me. – stefankmitph Apr 02 '15 at 14:49
  • In my opinion the answer is not useful at all, so I'd want to see it deleted, yes. The fact that an answer *works* doesn't mean that the answer is *useful*. An answer that works, but causes major problems for the user, can often be more harmful than not having a solution. And given that there are other solutions that *aren't* so terrible, that's not even the choice that needs to be made. – Servy Apr 02 '15 at 14:51
  • alright, take Jonesy's answer. he got the most upvotes (i upvoted too) of all the answers here... his implementation is even worse (in my opinion). should he also delete his answer? (no, i'm not saying he should) – stefankmitph Apr 02 '15 at 14:55
  • I am, yes. It's *actively harmful* as an answer. That it has gotten so many upvotes for being such a terrible solution is highly unfortunate. Some poor soul might actually end up using it as a result. – Servy Apr 02 '15 at 14:56