2012-10-17 20 views
8

有沒有辦法與linq做到這一點,而不用枚舉fooCollection兩次?Linq在一次迭代中選擇和聚合

var fooCollection = // get foo 
var selectedIds = new List<int>(); 
var aggregateContent = String.Empty; 

foreach (var f in foo) 
{ 
    selectedIds.Add(foo.Id); 
    aggregateContent += foo.Content 
} 

var results = new FooResults 
{ 
    Content = aggregateContent, 
    SelectedIds = selectedIds 
}; 

return results; 

回答

10

是的,你可以使用Enumerable.Aggregate方法:

var result = fooCollection.Aggregate(new FooResult(), 
            (r,f) => 
            { 
             r.SelectedIds.Add(f.Id); 
             r.Content += f.Content; 
             return r; 
            }); 

這具有無副作用的優點。我不喜歡LINQ中的副作用。 =)

+0

我認爲你在那裏釘了它。謝謝。 – Nick

+0

+1:很好的使用'Aggregate'方法。 TIL。 –

1

你可以這樣做:

foo.ForEach(x => { selectedIds.Add(x.Id); aggregateContent += x.Content; }); 

我建議不串聯內容轉換爲字符串,而是使用StringBuilder代替。

編輯

如果你沒有實現ForEachIEnumerable LINQ的擴展庫,這裏是一個方法,你可以使用:

public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action) 
{ 
    foreach(T item in enumeration) 
    { 
     action(item); 
    } 
} 
+1

請注意:'ForEach'不存在'IEnumerable '。這是'List '的成員方法。 –

+0

謝謝丹尼爾;我相應地擴展了我的答案。 –

+1

我第二@Daniel的評論:List.Foreach與linq無關。 (該問題要求提供LINQ解決方案)。 – 2012-10-17 14:42:06

2

有一個方法可行,但我認爲它是一個黑客:

var aggregateContent = String.Empty; 
var selectedIds = foo.Select(x => { aggregateContent += x.Content; 
            return x.Id; }) 
        .ToList(); 

我會去你已經有的循環。它比任何你能想到的LINQ解決方案都要乾淨得多。

+1

我明白你的意思 - 使用Linq來選擇和增加一些東西並不完全是CQRS。不過,我將使用Linq提供的聚合函數,因爲這表示我想要做的事情清楚,並且感覺不那麼黑客。謝謝。 – Nick

0

你要求的是有一個Linq語句產生兩個結果。 linq的整個想法是讓一個簡潔的函數式編程風格沒有副作用。

如果你想要多個結果和良好的性能,你不應該使用Linq,並使用一個普通的foreach。

+0

我同意我不保持與CQRS內聯,但通過使用聚合函數,我表達了一個函數,它僅*引起副作用 - 其結果是明確的。 – Nick