Можно ли суммировать () более одного значения в одном операторе linq ... без группировки? - PullRequest
1 голос
/ 11 сентября 2011

Данный класс:

public class FooAmounts
int Id
decimal Month1
decimal Month2
...
decimal Month12
decimal Year2
decimal Year3
decimal Future

У меня есть IEnumerable<FooAmounts> с 5 записями (Id's 1-5 достаточно забавно!)

Я хотел бы создать одну новую FooAmounts, которая будет иметь итоги за каждый месяц / год, но только там, где Id == 1 + Id == 2 например,

var footle = (from f in foolist
where f.Id == 1 || f.Id == 2
select new FooAmounts{
Month1 = Sum(month 1s),
Month2 = Sum(month 2s),
etc etc.
Future = Sum(futures)
).FirstOrDefault();

Я пытаюсь воссоздать этот t-sql:

select SUM(Month1) as Month1, SUM(Month2) as Month2, SUM(Month3) as Month3 from FooAmount where Id=1 or Id=2"

Я могу получить подобное, используя предложение group by f.Id into grp, но это оставляет меня с двумя предметами, и поэтому я не могу использовать First / FoD, то есть мне придется вручную их суммировать ... что, похоже, мне не хватает трюк где-то?

Примечание: FirstOrDefault () только там, поэтому я возвращаю один объект, а не перечисляемый, содержащий один объект. Я думаю (возможно, неправильно!), Что это всегда должно возвращать только один элемент ... есть только один результат Sum (), независимо от того, сколько элементов было включено в расчет.

1 Ответ

1 голос
/ 11 сентября 2011

Независимо от того, есть ли у вас одно или несколько значений, не имеет смысла брать первый результат из суммы.

Если у меня 10 человек, я могу найти возраст первого или сумму 100 * * всех из них, но я не могу сначала найти сумма возрастов. Так что вы можете сделать:

 var matches = fooList.Where(f => f.Id == 1 || f.Id == 2);

 var sum = new FooAmounts { Month1 = matches.Sum(f => f.Month1),
                            Month2 = matches.Sum(f => f>Month2),
                            ... };

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

 // Materialize the result so we only filter once
 var matches = fooList.Where(f => f.Id == 1 || f.Id == 2).ToList();

 var sum = new FooAmounts { Month1 = matches.Sum(f => f.Month1),
                            Month2 = matches.Sum(f => f>Month2),
                            ... };

Или же вы можете использовать агрегацию:

 var sum = fooList.Where(f => f.Id == 1 || f.Id == 2)
                  .Aggregate(new FooAmounts(), // Seed
                             (sum, item) => new FooAmounts {
                                 Month1 = sum.Month1 + item.Month1,
                                 Month2 = sum.Month2 + item.Month2,
                                 ...
                             });

Это будет повторять последовательность только один раз, и не будет создавать большой буфер в памяти, но создаст много экземпляров FooAmounts во время итерации.

Вы могли бы модифицировать аккумулятор на месте, конечно:

 var sum = fooList.Where(f => f.Id == 1 || f.Id == 2)
                  .Aggregate(new FooAmounts(), // Seed
                             (sum, item) => {
                                 sum.Month1 += item.Month1;
                                 sum.Month2 += item.Month2;
                                 ...
                                 return sum;
                             });

Мне кажется, немного противно, но на самом деле * не имеет побочных эффектов в плохой форме, поскольку это только мутирует объект, который все равно инициализируется в вызове .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...