2009-11-09 23 views
10

Mam pętlę jak poniżej, czy mogę zrobić to samo za pomocą wielu SUM?Wiele SUM przy użyciu 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 nie jest całkowitą manipulacją danymi i nie ma w niej nic złego w pętli for. – SLaks

+0

Zabawne chociaż @SLaks, że jest to jeden z nielicznych przypadków, w których LINQ nie oferuje rozsądnego rozwiązania. – PeterX

Odpowiedz

7

Technicznie rzecz biorąc, co masz jest prawdopodobnie najbardziej skuteczny sposób rób to, o co prosisz. Jednak można utworzyć metody rozszerzenie na IEnumerable <T> nazwie Każda które mogą uprościć:

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

i nazwać tak:

// 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: tylko jedna uwaga dodatkowa. Powyższy przykład opiera się na zamknięciu. Zmienne waga, długość i elementy powinny być zadeklarowane w zakresie nadrzędnym, pozwalając im zachować się poza każdym wywołaniem akcji itemWorker. Zaktualizowałem przykład, aby to odzwierciedlić dla jasności.

+0

Bardzo eleganckie rozwiązanie. – Alessandro

+0

Cieszę się z usług. :-) – jrista

+0

+1 Bardzo fajna technika. –

4

Możesz zadzwonić pod numer Sum trzy razy, ale będzie wolniej, ponieważ spowoduje utworzenie trzech pętli.

Na przykład:

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

powodu opóźnionej realizacji, będzie to również ponownej oceny połączenia Where za każdym razem, mimo że nie jest takim problemem w Twoim przypadku. Można tego uniknąć, wywołując ToArray, ale spowoduje to przydzielenie tablicy. (I nadal będzie działał trzy pętle)

Jednak, jeśli nie masz dużej liczby wpisów lub uruchamiasz ten kod w ciasnej pętli, nie musisz martwić się o wydajność.


EDIT: Jeśli naprawdę chcesz używać LINQ, można nadużywać Aggregate, tak:

int totalWeight, totalLength, items; 

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

To fenomenalnie brzydki kod, ale należy wykonać niemal jak również prosta pętla.

Można również podsumować akumulator, (patrz przykład poniżej), ale to przydzieli tymczasowy obiekt dla każdego elementu na liście, co jest głupim pomysłem. (Typy anonimowych są niezmienne)

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. Zdaję sobie sprawę, że nie ma prostego sposobu na to, używając LINQ. Wezmę pętlę foreach, ponieważ zrozumiałem, że nie jest tak źle. Dziękuję wszystkim. – Alessandro

+0

Czy możesz skomentować odpowiedź użytkownika user805138? Jak wygląda jego wygląd w jego podejściu? – gisek

+0

@gisek: "Grupa x przez 1" jest całkowicie bezużyteczna i bardzo głupia; wprowadza składnię LINQ bez żadnego powodu. Poza tym jest identyczny z moim pierwszym kodem; używa dwóch dodatkowych pętli. – SLaks

0

Ok. Zdaję sobie sprawę, że nie ma prostego sposobu na to, używając LINQ. Wezmę pętlę foreach, ponieważ zrozumiałem, że nie jest tak źle. Dzięki wam wszystkim

+0

Nie powinieneś publikować odpowiedź na SO tak, jakbyś opublikował odpowiedź na temat na forum. Zrób to tylko, jeśli odpowiesz na własne pytanie. Zwykle dodajesz * UPDATE * do oryginalnego pytania jako rodzaj odpowiedzi na odpowiedzi i komentarze na twoje pytanie. –

2

Można też grupa przez prawdziwe - 1 (co faktycznie jest w tym jednego z przedmiotów, a następnie je policzone lub summered):

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

miarę Czas trwania jest prawie tak dobre, jak pętla (ze stałym dodatkiem).

+0

"Grupowanie według" jest całkowicie bezużyteczne i raczej mylące. Po prostu wykonaj 'var results = new {... = ArticleLedgerEntries.Sum (...), ...}' – SLaks