4

I have an IEnumerable<IEnumerable<double>> which can basically be thought of as a 2D array of doubles and what I want to do is get a 1D array (or list or whatever) that is the total for all the columns of my original collection. In other words, if I had:

1   2   3   4
2   3   1   6
3   4   5   6
-------------
6   9   9   16

I'd want the last line (which is not part of the original collection, obviously).

Is there a simple way to do this with LINQ so that I don't have to loop over the whole thing? I know I can use .Sum to total one row or one column, but I want to total each row.

I've seen some other questions (mostly dealing with database queries) that suggest using group but that didn't seem to work for me. I tried this:

            var totals = from p in toTotal
                         group p by 1 into g
                         select g.Sum(t => t.First()); 

But that just totals everything.

Is there a clever way to do this?

Edit: for example, if toTotal was defined as:

    List<List<double>> toTotal = new List<List<double>>() {
        new List<double> {1,   2,   3,   4},
        new List<double> {2,   3 ,  1,   6},
        new List<double> {3 ,  4,   5 ,  6}
    };
Matt Burland
  • 44,552
  • 18
  • 99
  • 171

2 Answers2

4

Ok. I think I've hit on it:

var totals = toTotal.Aggregate((prev, next) => prev.Zip(next, (a, b) => a + b));

The trick was that I was looking for a Fold (or Reduce) function, but in LINQ it's called Aggregate.

Here's a fiddle showing this and @Habib's code for totaling rows (for comparison). The only change from what I have here (and what I was using in my actual code) is that I needed to .ToList the result of .Zip because for this test case I have a List<List<double>> and that seems to upset the compiler if you don't explicitly convert to a list. In my actual code toTotal is an IEnumerable<IEnumerable<double>> and doesn't need converting.

Matt Burland
  • 44,552
  • 18
  • 99
  • 171
3

EDIT:

You can do:

List<List<double>> toTotal = new List<List<double>>() {
new List<double> {1,   2,   3,   4},
new List<double> {2,   3 ,  1,   6},
new List<double> {3 ,  4,   5 ,  6}
                                };

var res = toTotal.Select(r => r.Select((t, i) => new { Column = i, Value = t }))
                 .SelectMany(r => r)
                 .GroupBy(r => r.Column)
                 .Select(grp => new
                 {
                     Column = grp.Key,
                     Sum = grp.Select(r => r.Value).Sum(),
                 });

foreach (var item in res)
{
    Console.WriteLine("Column: {0}, Sum: {1}", item.Column, item.Sum);
}

and you will get:

Column: 0, Sum: 6
Column: 1, Sum: 9
Column: 2, Sum: 9
Column: 3, Sum: 16

Old Answer

You need the Sum for each element of your IEnumerable toTotal you can get it like:

double[]totals = toTotal.Select(r => r.Sum()).ToArray();
Habib
  • 219,104
  • 29
  • 407
  • 436
  • Thanks, but that's not quite what I'm looking for. That will total the rows, not the columns. I need the sum of `r[0], r[1]...r[n]` – Matt Burland Apr 15 '14 at 15:56
  • @MattBurland, can you show how you have defined `toTotal` with some values. – Habib Apr 15 '14 at 15:57
  • 1
    @MattBurland, Just modified my answer, which is now summing up on column. But I must admit your answer looks elegant. – Habib Apr 15 '14 at 16:23
  • 1
    Thanks Habib, I actually wasn't aware that you can get `Select` to pass in the index like that, which is pretty nifty. – Matt Burland Apr 15 '14 at 16:46