【问题标题】:Can you Sum() more than one value in a single linq statement...without grouping?您可以在单个 linq 语句中 Sum() 多个值...不分组吗?
【发布时间】:2011-09-11 07:39:58
【问题描述】:

给定类:

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

我有 IEnumerable<FooAmounts> 有 5 个条目(ID 的 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() 只有一个结果,无论有多少项目进入计算。

【问题讨论】:

    标签: c# linq sum


    【解决方案1】:

    不管你有一个值还是多个值,取和的第一个结果是没有意义的。

    如果我有 10 个人,我可以找到第一个的年龄,或者我可以找到他们所有的年龄总和 - 但我找不到 第一个 年龄总和。所以你可以这样做:

     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;
                                 });
    

    这对我来说有点有点讨厌,但它真的并没有以不好的方式产生副作用,因为它只是改变了在还是打电话吧。

    【讨论】:

    • Apols,可能措辞不当。我只是想在最后得到一个包含总数的对象,而不是一个对象列表(我已经拥有)或者必须为每个月做一个 Sum(s=>s.property)...年我想要的。
    • @BlueChippy:我已经用更多选项编辑了我的帖子。我不认为除了这些选项之外还有什么简单而优雅的东西。
    • 感谢 Jon - 我曾希望重新创建此 t-sql 等效项“选择 SUM(Month1) as Month1, SUM(Month2) as Month2, SUM(Month3) as Month3 from FooAmount where Id=1 or ID=2" :)
    • 我选择了实体化版本 - 完成了这项工作,尽管不知何故让我觉得应该有一个单步的方法。
    猜你喜欢
    • 2014-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-16
    • 1970-01-01
    • 2020-02-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多