C# 如何使用LINQ对通用列表中的值求和?

C# 如何使用LINQ对通用列表中的值求和?,c#,linq,generics,C#,Linq,Generics,我有这门课: 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

我有这门课:

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;
…但所需的下一个单元格val是基于给定月份的所有TotalPurchases值计算得出的。因此,我需要计算具有相同月值的所有项目的“总百分比”值。在SQL中,我可以执行以下操作:

SELECT SUM(TotalPurchases) FROM ItemsForMonthYear WHERE monthYr = 'Apr 14'
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;
}
…然后将结果除以每个ItemDescription的TotalPurchases值

瞧,如果“4月14日”一个月的所有采购总额为100万美元,而“可可泡芙”项目的采购总额为10万美元,那么我可以计算出“可可泡芙”的总采购百分比为10%

我可以“蛮力”并循环浏览整个通用列表:

Decimal allTotalPurchases;
foreach (ItemsForMonthYear ifmy in itemsForMonthYearList)
{
    if (ifmy.monthYr.Equals("Apr 14") 
    {
        allTotalPurchases = allTotalPurchases + ifmy.TotalPurchases;
    }
}
…但我认为有一种更优雅/性能更友好的方法可以使用LINQ实现这一点。我想这样做:

SELECT SUM(TotalPurchases) FROM ItemsForMonthYear WHERE monthYr = 'Apr 14'
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“totPurchasesForMonthYear”将是上述示例中的十万美元,arg“totPurchasesForMonthYear”将是“Apr 14”等值


有人知道LINQ的聪明诀窍吗?

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

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;
所需的下一个单元格值是根据给定月份的所有TotalPurchases值计算得出的。因此,我需要计算具有相同月值的所有项目的“总百分比”值

首先,您需要获得具有相同的
monthYr

var allMonthYr = itemsForMonthYearList.Where(x => x.monthYr == ifmy.monthYr);
然后是一个简单的求和:

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];

马克·布莱克特(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);
}

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

这是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];
下面是我的答案,给出了一个
列表
,并返回了一个
列表
,在项目中正确填写了百分比:

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; }
}
输出:

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