如何使用一个 linq 查询获得总数量?

Posted

技术标签:

【中文标题】如何使用一个 linq 查询获得总数量?【英文标题】:How do I get total Qty using one linq query? 【发布时间】:2014-01-15 21:15:10 【问题描述】:

我有两个 linq 查询,一个是获取 confirmedQty,另一个是获取 unconfirmedQty

获取unconfirmedQty是有条件的。它应该是平均值而不是总和。

result = Sum(confirmedQty) + Avg(unconfirmedQty)

有没有办法只编写一个查询并获得所需的结果,而不是编写两个单独的查询?

我的代码

class Program

    static void Main(string[] args)
    
        List<Item> items = new List<Item>(new Item[]
        
            new Item Qty = 100, IsConfirmed=true ,
            new Item Qty = 40, IsConfirmed=false ,
            new Item Qty = 40, IsConfirmed=false ,
            new Item Qty = 40, IsConfirmed=false ,
        );

        int confirmedQty = Convert.ToInt32(items.Where(o => o.IsConfirmed == true).Sum(u => u.Qty));
        int unconfirmedQty = Convert.ToInt32(items.Where(o => o.IsConfirmed != true).Average(u => u.Qty));

        //Output => Total : 140
        Console.WriteLine("Total : " + (confirmedQty + unconfirmedQty));

        Console.Read();
    

    public class Item
    
        public int Qty  get; set; 
        public bool IsConfirmed  get; set; 
    

【问题讨论】:

【参考方案1】:

实际接受的答案会枚举您的项目集合2N + 1 次,它会为您的原始解决方案增加不必要的复杂性。如果我遇到了这段代码

(from t in items
 let confirmedQty = items.Where(o => o.IsConfirmed == true).Sum(u => u.Qty)
 let unconfirmedQty = items.Where(o => o.IsConfirmed != true).Average(u => u.Qty)
 let total = confirmedQty + unconfirmedQty
 select new  tl = total ).FirstOrDefault();

需要一些时间来了解您将项目投影到什么类型的数据。是的,这个查询是一个奇怪的投影。它创建SelectIterator 来投影序列的每个项目,然后它创建一些范围变量,这涉及两次迭代项目,最后它选择第一个投影项目。基本上,您已将原始查询包装成额外的无用查询:

items.Select(i => 
    var confirmedQty = items.Where(o => o.IsConfirmed).Sum(u => u.Qty);
    var unconfirmedQty = items.Where(o => !o.IsConfirmed).Average(u => u.Qty);
    var total = confirmedQty + unconfirmedQty;
    return new  tl = total ;
).FirstOrDefault();

Intent 隐藏在代码深处,您仍然有两个相同的嵌套查询。你可以在这里做什么?您可以简化您的两个查询,使它们更具可读性并清楚地显示您的意图

int confirmedTotal = items.Where(i => i.IsConfirmed).Sum(i => i.Qty);
// NOTE: Average will throw exception if there is no unconfirmed items!
double unconfirmedAverage = items.Where(i => !i.IsConfirmed).Average(i => i.Qty);
int total = confirmedTotal + (int)unconfirmedAverage;

如果性能比可读性更重要,那么你可以在单个查询中计算总和(为了可读性移动到扩展方法):

public static int Total(this IEnumerable<Item> items)

    int confirmedTotal = 0;
    int unconfirmedTotal = 0;
    int unconfirmedCount = 0;

    foreach (var item in items)
    
        if (item.IsConfirmed)
        
            confirmedTotal += item.Qty;
        
        else
        
            unconfirmedCount++;
            unconfirmedTotal += item.Qty;
        
    

    if (unconfirmedCount == 0)
        return confirmedTotal;
    // NOTE: Will not throw if there is no unconfirmed items
    return confirmedTotal + unconfirmedTotal / unconfirmedCount;

用法很简单:

items.Total();

顺便说一句,接受答案的第二个解决方案不正确。它返回正确的值只是一个巧合,因为您有所有未确认的项目都等于Qty。此解决方案计算总和而不是平均值。分组解决方案如下所示:

var total = 
    items.GroupBy(i => i.IsConfirmed)
         .Select(g => g.Key ? g.Sum(i => i.Qty) : (int)g.Average(i => i.Qty))
         .Sum();

在这里,您将项目分为两组 - 已确认和未确认。然后根据组键和两个组值的汇总计算总和或平均值。这也既不可读也不高效的解决方案,但它是正确的。

【讨论】:

很好的解释。非常感谢。

以上是关于如何使用一个 linq 查询获得总数量?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用Django ORM查询计算数量乘以价格

如何使用 SQL 查询将总数量和消耗数量转化为单个结果

如何使用 LINQ 查询项目,但也包括丢失的项目?

如何使用SQL查询将总数量和消耗的数量转换为单个结果

如何使用 LINQ to SQL 在计算列上获得不同的值?

如何获得两个查询结果集之间的差异