2009-11-09 5 views
10

次のようなループがありますが、複数のSUMを使用して同じことを行うことはできますか?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

LINQがbe-すべてのエンドすべてのデータ操作のではなく、ループのためには何の問題もまだありません。 – SLaks

+0

これは、LINQが妥当な解決法を提供していないまれなケースの1つです@SLaks。 – PeterX

答えて

7

技術的には、何を持っていることは、おそらく最も効率的な方法ですあなたが求めていることをやってください。しかし、あなたはそれが簡単にするかもしれない各呼ばれる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: ただ、一つの追加のノート。上記の例はクロージャに依存しています。変数weight、length、およびitemsは親スコープで宣言し、itemWorkerアクションの各呼び出しを超えて持続できるようにする必要があります。わかりやすくするために例を更新しました。

+0

非常に洗練されたソリューション。 – Alessandro

+0

喜んでサービスします。 :-) – jrista

+0

+1非常にクールなテクニック。 –

4

あなたはSum 3回呼び出すことができますが、それは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を呼び出すことで回避できますが、それは配列の割り当てを引き起こします。

ただし、非常に多数のエントリがある場合や、タイトループでこのコードを実行している場合を除き、パフォーマンスについて心配する必要はありません。


EDIT

int totalWeight, totalLength, items; 

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

これは驚異的に醜いコードですが、ほとんどと同様に実行する必要があります。あなた本当にはこのように、あなたがAggregateを誤用でき、LINQを使用したい場合ストレートループ。

また、(以下の例を参照)、アキュムレータにまとめることができますが、これはダムアイデアですあなたのリスト内のすべての項目のための一時的なオブジェクトを割り当てます。 (匿名型は不変です)あなたは、このトピックで答えを使って、このピボットスタイルを行うことができると思います

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

私はLINQを使ってこれを行う簡単な方法がないことを認識しています。私はそれがそれほど悪くないと理解したので、私はループをforeachするかもしれません。 皆様おかげさまで – Alessandro

+0

あなたはuser805138の回答にコメントしていただけますか?彼のアプローチではパフォーマンスはどのように見えますか? – gisek

+0

@gisek: 'group x by 1'は完全に役に立たず、とても愚かです。 LINQ構文をまったく理由なしで導入しています。それ以外は、私の最初のコードと同じです。 2つの余分なループを使用します。 – SLaks

0

私はLINQを使ってこれを行う簡単な方法がないことを認識しています。私はそれがそれほど悪くないと理解したので、私はループをforeachするかもしれません。

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

限りの時間を実行しているとして、それがある:1(実際の項目のいずれかとし、それらを数えたりsummeredているなどの場合) - 真によってあなたは可能性もグループあなたの

+0

あなたがフォーラムのトピックに関する返信を投稿するように、そう答えてください。あなた自身の質問に答える場合にのみこれを実行してください。通常、元の質問に* UPDATE *を追加します。これは、あなたの質問に対する回答やコメントへの回答の一種です。 –

2

すべてに感謝ループとほぼ同じくらい(定数が追加されています)。

+0

'group by 'はまったく役に立たず、むしろ混乱します。 'var results = new {... = ArticleLedgerEntries.Sum(...)、...}'を実行するだけです。 – SLaks