【问题标题】:How can I sum values across a generic list using LINQ?如何使用 LINQ 对通用列表中的值求和?
【发布时间】:2015-11-07 02:10:25
【问题描述】:

我有这门课:

public class ItemsForMonthYear
{
    public String ItemDescription { get; set; }
    public String monthYr { get; set; }
    public int TotalPackages { get; set; }
    public Decimal TotalPurchases { get; set; }
    public Decimal AveragePrice { get; set; }
    public Double PercentOfTotal { get; set; }
}

...我存储数据的地方。我有一个用于存储其中 N 个的通用列表:

List<ItemsForMonthYear> itemsForMonthYearList;

...然后我从该列表中读取以填充电子表格,这非常简单:

totPackagesCell.Value2 = ifmy.TotalPackages;
totPurchasesCell.Value2 = ifmy.TotalPurchases;
avgPriceCell.Value2 = ifmy.AveragePrice;

...但所需的下一个单元格 val 是根据给定月份的所有 TotalPurchases 值计算得出的。因此,我需要为具有相同月值的所有 ItemsForMonthYear “记录”计算“总百分比”值。在 SQL 中,我可以这样做:

SELECT SUM(TotalPurchases) FROM ItemsForMonthYear WHERE monthYr = 'Apr 14'

...然后将该结果除以每个 ItemDescription 的 TotalPurchases 值

IOW,如果“4 月 14 日”月份的所有 TotalPurchases 总和为 100 万美元,并且 ItemDescription“Cocoa Puffs”的 TotalPurchases 为 10 万美元,我可以计算“Cocoa Puffs”的 PercentOfTotal 为为 10%。

我可以“暴力破解”并遍历整个通用列表:

Decimal allTotalPurchases;
foreach (ItemsForMonthYear ifmy in itemsForMonthYearList)
{
    if (ifmy.monthYr.Equals("Apr 14") 
    {
        allTotalPurchases = allTotalPurchases + ifmy.TotalPurchases;
    }
}

...但我认为有一种更优雅/性能更友好的方式可以使用 LINQ 完成此任务。我想做这样的事情:

percentOfTotalCell.Value2 = GetPercentForPurchaseForMonthYearAsStr(ifmy.TotalPurchases, curMonth);

private String GetPercentForPurchaseForMonthYearAsStr(decimal totPurchasesForItemMonthYear, string totPurchasesForMonthYear)
{    
    percentOfTotal = // What LINQ magic goes here?
    percentOfTotalStr = percentOfTotal.ToString("P", CultureInfo.InvariantCulture);
    return percentOfTotalStr;
}

...其中 arg "totPurchasesForItemMonthYear" 将是上面示例中的十万美元等值,而 arg "totPurchasesForMonthYear" 将是值例如“4 月 14 日”。

有谁知道这个聪明的 LINQ 技巧?

【问题讨论】:

    标签: c# linq generics


    【解决方案1】:

    所需的下一个单元格 val 是根据给定月份的所有 TotalPurchases 值计算得出的。所以我需要为所有具有相同月值的 ItemsForMonthYear “记录”计算“总百分比”值。

    嗯,首先,您需要获得具有相同monthYr 的那些:

    var allMonthYr = itemsForMonthYearList.Where(x => x.monthYr == ifmy.monthYr);
    

    那么就是简单的Sum

    var totalPurchasesOfMonthYr = allMonthYr.Sum(x => x.TotalPurchases);
    var percentOfTotal = ifmy.TotalPurchases / totalPurchasesOfMonthYr;
    

    现在,这被证明是非常低效的——因为您不必要地重复这个列表很多次。最好先分组然后求和一次:

    var groupedItemsForMonthYr = itemsForMonthYearList.ToLookup(x => x.monthYr);
    var totalsForMonthYr = groupedItemsForMonthYr.ToDictionary(
        x => x.Key, 
        x => x.Sum(x => x.TotalPurchases)
    );
    
    var percentOfTotal = ifmy.TotalPurchases / totalsForMonthYr[ifmy.monthYr];
    

    【讨论】:

    • 分组代码看起来很有希望,但我得到三个错误:(1)无法将 lambda 表达式转换为类型 'System.Collections.Generic.IEqualityComparer' 因为它不是委托类型 (2) 不能在此范围内声明名为“x”的局部变量,因为它会给“x”赋予不同的含义,后者已在“父或当前”范围中用于表示其他内容 (3) 运算符/' 不能应用于 'decimal' 和 'System.Linq.IGrouping' 类型的操作数
    • 查看我的回答,了解我认为上述代码需要修复才能使其正常运行。
    【解决方案2】:

    您可以这样做来计算一个月的总购买量:

    decimal totalPurchases = itemsForMonthYearList.Where(item => item.monthYr == "Apr 14")
                                                  .Sum(item => item.TotalPurchases);
    

    然后您可以通过以下方式计算给定项目的百分比:

    decimal percentage = (itemsForMonthYearList.Single(item => item.ItemDescription == "Cocoa Puffs")
                                               .TotalPurchases / totalPurchases) * 100;
    

    【讨论】:

      【解决方案3】:

      这是 Mark Brackett 的回答,我认为这是适当的修复:

      // This is code that goes outside your loop
      var groupedItemsForMonthYr=itemsForMonthYearList.GroupBy(x=>x.monthYr);
      var totalsForMonthYr=groupedItemsForMonthYr.ToDictionary(
          x => x.Key, 
          x => x.Sum(y => y.TotalPurchases)
      );
      
      // This is the code that goes inside your loop
      var percentOfTotal = ifmy.TotalPurchases / totalsForMonthYr[ifmy.monthYr];
      

      这是我的答案,给出了 List&lt;ItemsForMonthYear&gt; 并在正确填写 PercentOfTotal 的项目中返回 List&lt;ItemsForMonthYear&gt;

      var items=itemsForMonthYearList.GroupBy(x=>x.monthYr)
        .Select(x=>new { total=x.Sum(y=>y.TotalPurchases), i=x })
        .Select(x=>x.i.Select(y=>new ItemsForMonthYear {
              ItemDescription=y.ItemDescription,
              monthYr=y.monthYr,
              TotalPackages=y.TotalPackages,
              TotalPurchases=y.TotalPurchases,
              AveragePrice=y.AveragePrice,
              PercentOfTotal=(double)y.TotalPurchases/(double)x.total
            }))
        .SelectMany(x=>x);
      

      这是我的测试代码:

      void Main()
      {
          var itemsForMonthYearList=new[]{
              new ItemsForMonthYear { ItemDescription="A",monthYr="Aug 2014",TotalPackages=1,TotalPurchases=1,AveragePrice=1},
              new ItemsForMonthYear { ItemDescription="A",monthYr="Sep 2014",TotalPackages=1,TotalPurchases=1,AveragePrice=1},
              new ItemsForMonthYear { ItemDescription="A",monthYr="Sep 2014",TotalPackages=1,TotalPurchases=1,AveragePrice=1},
              new ItemsForMonthYear { ItemDescription="A",monthYr="Oct 2014",TotalPackages=1,TotalPurchases=1,AveragePrice=1},
              new ItemsForMonthYear { ItemDescription="A",monthYr="Oct 2014",TotalPackages=1,TotalPurchases=1,AveragePrice=1},
              new ItemsForMonthYear { ItemDescription="A",monthYr="Oct 2014",TotalPackages=1,TotalPurchases=1,AveragePrice=1},
          };
      
          var items=itemsForMonthYearList.GroupBy(x=>x.monthYr)
            .Select(x=>new { total=x.Sum(y=>y.TotalPurchases), i=x })
            .Select(x=>x.i.Select(y=>new ItemsForMonthYear {
                  ItemDescription=y.ItemDescription,
                  monthYr=y.monthYr,
                  TotalPackages=y.TotalPackages,
                  TotalPurchases=y.TotalPurchases,
                  AveragePrice=y.AveragePrice,
                  PercentOfTotal=(double)y.TotalPurchases/(double)x.total
                }))
            .SelectMany(x=>x);
            items.Dump();
      }
      
      public class ItemsForMonthYear
      {
          public String ItemDescription { get; set; }
          public String monthYr { get; set; }
          public int TotalPackages { get; set; }
          public Decimal TotalPurchases { get; set; }
          public Decimal AveragePrice { get; set; }
          public Double PercentOfTotal { get; set; }
      }
      

      输出:

      【讨论】:

        【解决方案4】:
        decimal allTotalPurchases = itemsForMonthYearList.Where(s => s.monthYr.Equals("Apr 14")).Select(s => s.TotalPurchases).Sum();
        

        【讨论】:

          【解决方案5】:

          这段代码来自 Mark Brackett 自称“效率极低”的第一次尝试,运行良好:

          private String GetPercentForPurchaseForMonthYearAsStr(decimal totPurchasesForItemMonthYear, string monthToCalculate)
          {
              var allRecordsForMonthYr = itemsForMonthYearList.Where(x => x.monthYr == monthToCalculate);
              var totalPurchasesOfMonthYr = allRecordsForMonthYr.Sum(x => x.TotalPurchases);
              var percentOfTotal = totPurchasesForItemMonthYear / totalPurchasesOfMonthYr;
              return percentOfTotal.ToString("P", CultureInfo.CurrentCulture);
          }
          

          由于分组代码无法为我编译,我对上面的代码很满意。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-05-01
            相关资源
            最近更新 更多