首页 > 解决方案 > 分组和求和

问题描述

我有一个如下列表,其中将包含以下 poco 类。

public class BoxReportView
{
    public DateTime ProductionPlanWeekStarting { get; set; }
    public DateTime ProductionPlanWeekEnding { get; set; }
    public string BatchNumber { get; set; }
    public string BoxRef { get; set; }
    public string BoxName { get; set; }
    public decimal Qty { get; set; }

    public FUEL_KitItem KitItem { get; set; }
    public decimal Multiplier { get; set; }
}

我想通过使用 BoxName 和 Qty 对报告进行分组并求和所以我尝试了以下

var results = from line in kitItemsToGroup
              group line by line.BoxName into g
              select new BoxReportView
              {
                  BoxRef = g.First().BoxRef,
                  BoxName = g.First().BoxName,                                          
                  Qty = g.Count()
               };

在我的旧报告中,我只是这样做

var multiplier = finishedItem.SOPOrderReturnLine.LineQuantity - 
                 finishedItem.SOPOrderReturnLine.StockUnitDespatchReceiptQuantity;

foreach (KitItem kItem in kitItems.Cast<KitItem().Where(z => z.IsBox == true).ToList())
{
   kittItemsToGroup.Add(new BoxReportView() {
       BatchNumber = _batchNumber,
       ProductionPlanWeekEnding = _weekEndDate,
       ProductionPlanWeekStarting = _weekStartDate,
       BoxRef = kItem.StockCode,
       KitItem = kItem,
       Multiplier = multiplier,
       Qty = kItem.Qty });
   }
}

然后我刚回来

return kitItemsToGroup;

但是当我将它用作 var 时,我无法通过框名和数量来处理分组和总和的最佳方法。

标签: c#linq

解决方案


这是否是最好的方法取决于您的优先级。处理速度重要,还是代码易于理解、易于测试、易于更改和易于调试更重要?

LINQ 的优点之一是,它尽量避免不必要地枚举源。

您确定此代码的用户将始终需要完整的集合吗?会不会是现在,或者在不久的将来,有人只想要第一个元素?还是在他获取第 20 个元素并发现没有任何感兴趣的内容后决定停止枚举?

使用 LINQ 时,尽量返回IEnumerable<...>。只有将解释您的 LINQed 数据的最终用户决定他是否只想获取FirstOrDefault()、 或Count()所有内容,或者将其放入字典中,或其他任何内容。如果不将其用作列表,则创建列表是浪费处理能力。

你的 LINQ 代码和你的 foreach 做一些完全不同的事情。唉,在 StackOverflow 上,人们在没有真正指定他们的要求的情况下要求 LINQ 语句是很常见的。所以我必须在你的 LINQ 语句和你的 foreach 之间猜测一些东西。

需求将 的输入序列分组kitItems,期望将它们Fuel_KitItems分成BoxReportViews具有相同 的组,并从每组中的每个中BoxName选择几个属性。Fuel_KitItem

var kitItemGroups = kitItems
    .Cast<Fuel_KitItem>()       // only needed if kitItems is not IEnumerable<Fuel_KitItem>
    // make groups of Fuel_KitItems with same BoxName:
    .GroupBy(fuelKitItem => fuelKitItem.BoxName,

        // ResultSelector, take the BoxName and all fuelKitItems with this BoxName:
        (boxName, fuelKitItemsWithThisBoxName) => new
        {
            // Select only the properties you plan to use:
            BoxName = boxName,

            FuelKitItems = fuelKitItemsWithThisBoxName.Select(fuelKitItem => new
            {
                 // Only Select the properties that you plan to use
                 BatchNumber = fuelKitItem.BatchNumber,
                 Qty = fuelKitItem.Qty,
                 ...

                 // Not needed, they are all equal to boxName:
                 // BoxName = fuelKitItem.BoxName
             })
            // only do ToList if you are certain that the user of the result
            // will need the complete list of fuelKitItems in this group
            .ToList(),
         });

用法:

var kitItemGroups = ...

// I only need the KitItemGroups with a BoxName starting with "A"
var result1 = kitItemGroups.Where(group => group.BoxName.StartsWith("A"))
    .ToList();

// Or I only want the first three after sorting by group size
var result2 = kitItemGroups.OrderBy(group => group.FuelKitItems.Count())
    .Take(3)
    .ToList();

效率改进:只要您不知道如何使用您的 LINQ,就不要将其设为列表。如果您知道需要 group.FuelKitItems 的计数的可能性很高,则到 ToList


推荐阅读