Несколько СУММ с помощью LINQ
У меня есть цикл вроде следующего: могу ли я сделать то же самое с помощью нескольких 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;
}
Ответы
Ответ 1
Технически говоря, то, что у вас есть, - это, пожалуй, самый эффективный способ сделать то, что вы просите. Однако вы можете создать метод расширения в IEnumerable <T> Каждый может сделать это проще:
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;
}
);
UPDATE:
Еще одно примечание. Вышеприведенный пример основан на закрытии. Вес переменных, длина и элементы должны быть объявлены в родительской области, что позволяет им оставаться за каждым вызовом к действию itemWorker. Я обновил этот пример, чтобы отразить это ради ясности.
Ответ 2
Вы можете вызвать Sum
три раза, но он будет медленнее, потому что он сделает три цикла.
Например:
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
, но это вызовет распределение массива. (И он все равно будет запускать три цикла)
Однако, если у вас не очень большое количество записей или этот код работает в узком цикле, вам не нужно беспокоиться о производительности.
EDIT. Если вы действительно хотите использовать 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
}
);
Ответ 3
Вы также можете группировать 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)
};
Что касается времени выполнения, он почти так же хорош, как и цикл (с постоянным добавлением).
Ответ 4
Вы сможете сделать этот сводный стиль, используя ответ в этом разделе: Возможно ли сворачивать данные с помощью LINQ?
Ответ 5
Ok. Я понимаю, что нет простого способа сделать это с помощью LINQ. Я возьму цикл foreach, потому что понял, что это не так плохо. Спасибо всем вам.