2009-11-09 45 views
10

Tôi có một vòng lặp như sau, tôi có thể làm tương tự bằng cách sử dụng nhiều SUM không?Nhiều SUM bằng LINQ

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

LINQ không phải là tất cả và kết thúc tất cả các thao tác dữ liệu, và vẫn không có gì sai với vòng lặp for. – SLaks

+0

Hài hước mặc dù @SLaks rằng đây là một trong những trường hợp hiếm hoi mà LINQ không cung cấp một giải pháp hợp lý. – PeterX

Trả lời

7

Về mặt kỹ thuật, những gì bạn có lẽ là cách hiệu quả nhất để làm những gì bạn đang hỏi. Tuy nhiên, bạn có thể tạo ra một phương pháp mở rộng trên IEnumerable <T> gọi Mỗi mà có thể làm cho nó đơn giản hơn:

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

Và gọi nó như vậy:

// 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: Chỉ cần một lưu ý bổ sung. Ví dụ trên dựa vào việc đóng cửa. Các biến trọng lượng, độ dài và các mục cần được khai báo trong phạm vi cấp độ gốc, cho phép chúng tồn tại lâu hơn mỗi cuộc gọi đến hành động itemWorker. Tôi đã cập nhật ví dụ để phản ánh điều này vì mục đích rõ ràng.

+0

Giải pháp rất thanh lịch. – Alessandro

+0

Rất vui được phục vụ. :-) – jrista

+0

+1 Kỹ thuật rất hay. –

4

Bạn có thể gọi Sum ba lần, nhưng sẽ chậm hơn vì sẽ thực hiện ba vòng.

Ví dụ:

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

Bởi vì thực hiện chậm, nó cũng sẽ đánh giá lại các Where gọi mọi thời gian, mặc dù đó không phải là như vậy một vấn đề trong trường hợp của bạn. Điều này có thể tránh được bằng cách gọi ToArray, nhưng điều đó sẽ gây ra một phân bổ mảng. (Và nó vẫn chạy ba vòng)

Tuy nhiên, trừ khi bạn có số lượng mục nhập rất lớn hoặc đang chạy mã này trong một vòng lặp chặt chẽ, bạn không cần phải lo lắng về hiệu suất.


EDIT: Nếu bạn thực sự muốn sử dụng LINQ, bạn có thể lạm dụng Aggregate, như thế này:

int totalWeight, totalLength, items; 

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

Đây là mã phi thường xấu xí, nhưng nên thực hiện hầu như cũng như một vòng lặp thẳng.

Bạn cũng có thể tính tổng trong bộ tích lũy, (xem ví dụ bên dưới), nhưng điều này sẽ phân bổ một đối tượng tạm thời cho mọi mục trong danh sách của bạn, đó là một ý tưởng ngớ ngẩn. (Loại Anonymous là không thay đổi)

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 
    } 
); 
+0

Ok. Tôi nhận ra rằng không có một cách dễ dàng để làm điều này bằng cách sử dụng LINQ. Tôi sẽ thực hiện vòng lặp foreach vì tôi hiểu rằng nó không quá tệ. Nhờ tất cả các bạn. – Alessandro

+0

Bạn có thể vui lòng nhận xét về câu trả lời của user805138 không? Hiệu suất trông như thế nào trong cách tiếp cận của anh ấy? – gisek

+0

@gisek: Nhóm 'x 1' hoàn toàn vô dụng và rất ngu xuẩn; nó giới thiệu cú pháp LINQ không có lý do gì cả. Ngoài ra, nó giống với mã đầu tiên của tôi; nó sử dụng hai vòng phụ. – SLaks

0

Ok. Tôi nhận ra rằng không có một cách dễ dàng để làm điều này bằng cách sử dụng LINQ. Tôi sẽ thực hiện vòng lặp foreach vì tôi hiểu rằng nó không quá tệ. Nhờ tất cả các bạn

+0

Bạn không nên đăng câu trả lời trên SO như bạn sẽ đăng trả lời về một chủ đề trong một diễn đàn. Chỉ làm điều này nếu bạn trả lời câu hỏi của riêng bạn. Thông thường, bạn thêm * UPDATE * vào câu hỏi ban đầu của bạn như một loại trả lời cho câu trả lời và nhận xét về câu hỏi của bạn. –

2

Bạn cũng có thể nhóm theo đúng - 1 (mà thực sự là bao gồm bất kỳ các mục và sau đó họ đã tính hoặc nghỉ hè):

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) 
            }; 

As far as Chạy thời gian, nó là gần như tốt như vòng lặp (với một bổ sung liên tục).

+0

'nhóm by' hoàn toàn vô dụng và khá khó hiểu. Chỉ cần thực hiện 'var results = new {... = ArticleLedgerEntries.Sum (...), ...}' – SLaks