10

我有一个类似下面的循环,我可以使用多个 SUM 来做同样的事情吗?

foreach (var detail in ArticleLedgerEntries.Where(pd => pd.LedgerEntryType == LedgerEntryTypeTypes.Unload &&
                                                                pd.InventoryType == InventoryTypes.Finished))
{
     weight += detail.GrossWeight;
     length += detail.Length;
     items  += detail.NrDistaff;
}
4

5 回答 5

8

从技术上讲,您所拥有的可能是完成您所要求的最有效的方法。但是,您可以在 IEnumerable<T> 上创建一个名为 Each 的扩展方法,这可能会使它更简单:

public static class EnumerableExtensions
{
    public static void Each<T>(this IEnumerable<T> col, Action<T> itemWorker)
    {
        foreach (var item in col)
        {
            itemWorker(item);
        }
    }
}

并这样称呼它:

// Declare variables in parent scope
double weight;
double length;
int items;

ArticleLedgerEntries
    .Where(
        pd => 
           pd.LedgerEntryType == LedgerEntryTypeTypes.Unload &&
           pd.InventoryType == InventoryTypes.Finished
    )
    .Each(
        pd => 
        {
            // Close around variables defined in parent scope
            weight += pd.GrossWeight; 
            lenght += pd.Length;
            items += pd.NrDistaff;
        }
    );

更新:只有一个附加说明。上面的例子依赖于一个闭包。变量 weight、length 和 items 应该在父范围中声明,允许它们在每次调用 itemWorker 操作之后持续存在。为了清楚起见,我更新了示例以反映这一点。

于 2009-11-09T23:00:11.277 回答
5

您可以调用Sum3 次,但会慢一些,因为它会产生 3 个循环。

例如:

var list = ArticleLedgerEntries.Where(pd => pd.LedgerEntryType == LedgerEntryTypeTypes.Unload
                                   && pd.InventoryType == InventoryTypes.Finished))

var totalWeight = list.Sum(pd => pd.GrossWeight);
var totalLength = list.Sum(pd => pd.Length);
var items = list.Sum(pd => pd.NrDistaff); 

由于延迟执行,它还会Where每次都重新评估调用,尽管在您的情况下这不是一个问题。这可以通过调用来避免ToArray,但这会导致数组分配。(它仍然会运行三个循环)

但是,除非您有大量的条目或在紧密循环中运行此代码,否则您无需担心性能。


编辑:如果你真的想使用 LINQ,你可能会误用Aggregate,像这样:

int totalWeight, totalLength, items;

list.Aggregate((a, b) => { 
    weight += detail.GrossWeight;
    length += detail.Length;
    items  += detail.NrDistaff;
    return a;
});

这是非常难看的代码,但执行起来应该几乎和直接循环一样好。

您也可以在累加器中求和(参见下面的示例),但这会为列表中的每个项目分配一个临时对象,这是一个愚蠢的想法。(匿名类型是不可变的)

var totals = list.Aggregate(
    new { Weight = 0, Length = 0, Items = 0},
    (t, pd) => new { 
        Weight = t.Weight + pd.GrossWeight,
        Length = t.Length + pd.Length,
        Items = t.Items + pd.NrDistaff
    }
);
于 2009-11-09T22:10:22.857 回答
2

您还可以按 true - 1 分组(实际上包括任何项目,然后对它们进行计数或求和):

 var results = from x in ArticleLedgerEntries
                       group x by 1
                       into aggregatedTable
                       select new
                                  {
                                      SumOfWeight = aggregatedTable.Sum(y => y.weight),
                                      SumOfLength = aggregatedTable.Sum(y => y.Length),
                                      SumOfNrDistaff = aggregatedTable.Sum(y => y.NrDistaff)
                                  };

就运行时间而言,它几乎与循环一样好(不断添加)。

于 2011-10-30T05:37:36.843 回答
0

您可以使用本主题中的答案来执行这种数据透视样式:Is it possible to Pivot data using LINQ?

于 2009-11-09T22:21:39.887 回答
0

行。我意识到使用 LINQ 没有一种简单的方法可以做到这一点。我会使用 may foreach 循环,因为我知道它还不错。感谢大家

于 2009-11-10T08:39:51.697 回答