Как объединить/суммировать записи по группам с помощью LINQ?

Например, как я могу группировать следующие записи с помощью GroupId с помощью LINQ и суммировать все остальные столбцы в каждой группе? (таким образом, объединение всех строк в каждой группе в одну)

var list = new List<Foo>()
{ 
    new Foo() { GroupId = 0, ValueA = 10, ValueB = 100 },
    new Foo() { GroupId = 1, ValueA = 30, ValueB = 700 },
    new Foo() { GroupId = 1, ValueA = 40, ValueB = 500 },
    new Foo() { GroupId = 2, ValueA = 80, ValueB = 300 },
    new Foo() { GroupId = 2, ValueA = 20, ValueB = 200 },
    new Foo() { GroupId = 2, ValueA = 20, ValueB = 200 }
};

Ожидаемый результат:

| GroupId | ValueA | ValueB |
|---------|--------|--------|
|    0    |   10   |   100  |
|    1    |   70   |   1200 |
|    2    |   120  |   700  |

Ответ 1

list.GroupBy(i => i.GroupId)
    .Select(g => new { GroupId = g.Key, 
                       ValueA = g.Sum(i => i.ValueA), 
                       ValueB = g.Sum(i => i.ValueB)});

или просто для удовольствия вы можете сделать это за один вызов GroupBy, используя свою перегрузку:

list.GroupBy(i => i.GroupId,
       (key, groupedItems) => new {
                                      GroupId = key,
                                      ValueA = groupedItems.Sum(i => i.ValueA),
                                      ValueB = groupedItems.Sum(i => i.ValueB),
                                  });

или вы можете использовать Aggregate, чтобы избежать повторения каждой группы много раз:

list.GroupBy(i => i.GroupId)
    .Select(g => g.Aggregate((i1, i2) => new Foo{ GroupId = i1.GroupId,
                                                  ValueA = i1.ValueA + i2.ValueA,
                                                  ValueB = i1.ValueB + i2.ValueB,
                                                }));

Ответ 2

var query = list.GroupBy(x=> x.GroupId)
                .Select(g=> new 
                  {
                     GroupId = g.Key, 
                     ValueA = g.Sum(x => x.ValueA), 
                     ValueB = g.Sum(x => x.ValueB)
                  });

Ответ 3

Используйте GroupBy для сбора Foo в группы на основе их GroupId, а затем создайте новый объект для каждой "строки" результата (этот объект может быть Foo сам на основе кода, который вы дайте, но так же легко может быть что угодно, включая анонимный тип). В этот момент вы также суммируете другие значения.

var sums = list.GroupBy(f => f.GroupId)
               .Select(g => new Foo
               { 
                   GroupId = g.Key, 
                   ValueA = g.Sum(f => f.ValueA),
                   ValueB = g.Sum(f => f.ValueB)
               }).ToList();

Ответ 4

Избегайте лямбда-выражений (почти) и используя "чисто" способ LINQ:

var sums = from foo in list
    group foo by foo.GroupId into groupings
    orderby groupings.Key ascending
    select new
    {
        GroupId = groupings.Key,
        ValueA = groupings.Sum(g => g.ValueA),
        ValueB = groupings.Sum(g => g.ValueB)
    };

Я просто думаю, что LINQ выглядит немного естественнее, чем лямбда (не то, чтобы что-то не так с этим...)