Can you Sum() more than one value in a single linq statement...without grouping?
Given class:
public class FooAmo开发者_运维百科unts
int Id
decimal Month1
decimal Month2
...
decimal Month12
decimal Year2
decimal Year3
decimal Future
I have IEnumerable<FooAmounts>
with 5 entries (Id's 1-5 funnily enough!)
I'd like to create a single new FooAmounts which has the totals of each month/year but only where Id==1 + Id==2 e.g.
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();
I am trying to re-create this t-sql:
select SUM(Month1) as Month1, SUM(Month2) as Month2, SUM(Month3) as Month3 from FooAmount where Id=1 or Id=2"
I can acheive similar using a group by f.Id into grp
clause but that leaves me with two items and therefore I can't use First/FoD, meaning I would have to manually Sum them still...which seems like I must be missing a trick somewhere?
Note: The FirstOrDefault() is only there so I get back a single object rather than an enumerable containing one object. I think (perhaps wrongly!) that this should always return only one item anyway...there is only one result of a Sum(), regardless as to how many items went into the calculation.
Regardless of whether you have one value or many, it doesn't make sense to take the first result of a sum.
If I have 10 people, I can find the age of the first one, or I can find the sum of the ages of all of them - but I can't find the first sum of the ages. So you can do:
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),
... };
Now that will execute the query multiple times, of course. You can materialize the query instead, and then do the summing over that:
// 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),
... };
Or alternatively you could use aggregation:
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,
...
});
This will only iterate over the sequence once, and not create a big buffer in memory, but will create a lot of FooAmounts
instances as it iterates.
You could modify the accumulator in place, of course:
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;
});
That's feels slightly nasty to me, but it doesn't really have side-effects in a bad way, as it's only mutating the object which is initialized within the call anyway.
精彩评论