2009-11-09 25 views
10

Tengo un ciclo como el siguiente, ¿puedo hacer lo mismo con múltiples SUM?SUM múltiple utilizando 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 no es el único y final de la manipulación de datos, y todavía no hay nada de malo con un bucle for. – SLaks

+0

Aunque gracioso @SLaks, este es uno de los pocos casos en que LINQ no ofrece una solución razonable. – PeterX

Respuesta

7

punto de vista técnico, lo que tienes es probablemente la forma más eficiente de haz lo que estás preguntando Sin embargo, se podría crear un método de extensión en IEnumerable <T> llamado a cada uno que podría hacerlo más sencillo:

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

y lo llaman así:

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

ACTUALIZACIÓN: Sólo una nota adicional. El ejemplo anterior se basa en un cierre. Las variables peso, longitud y elementos deben declararse en un ámbito principal, lo que les permite persistir más allá de cada llamada a la acción itemWorker. He actualizado el ejemplo para reflejar esto por claridad.

+0

Solución muy elegante. – Alessandro

+0

Me alegro de estar de servicio. :-) – jrista

+0

+1 Técnica muy genial. –

4

Puede llamar Sum tres veces, pero será más lento, ya que hará tres bucles.

Por ejemplo:

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

Debido a la ejecución retardada, que también volverá a evaluar la Where llamada cada vez, aunque eso no es un problema tal en su caso. Esto podría evitarse llamando al ToArray, pero eso causará una asignación de matriz. (Y todavía correría tres bucles)

Sin embargo, a menos que tenga una gran cantidad de entradas o esté ejecutando este código en un bucle cerrado, no necesita preocuparse por el rendimiento.


EDITAR: Si realmente desea utilizar LINQ, se podría mal uso Aggregate, así:

int totalWeight, totalLength, items; 

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

Este es el código extraordinariamente feo, pero debe realizar casi tan bien como lazo recto.

También podría sumar en el acumulador, (vea el ejemplo a continuación), pero esto asignaría un objeto temporal para cada elemento en su lista, lo cual es una idea tonta. (Los tipos anónimos son inmutables)

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. Me doy cuenta de que no hay una manera fácil de hacerlo con LINQ. Tomaré may foreach loop porque entendí que no es tan malo. Gracias a todos ustedes. – Alessandro

+0

¿Podría comentar la respuesta del usuario805138? ¿Cómo se ve la actuación en su enfoque? – gisek

+0

@gisek: El 'grupo x por 1' es completamente inútil y muy estúpido; introduce la sintaxis LINQ sin ningún motivo. Aparte de eso, es idéntico a mi primer código; usa dos bucles adicionales. – SLaks

0

Ok. Me doy cuenta de que no hay una manera fácil de hacerlo con LINQ. Tomaré may foreach loop porque entendí que no es tan malo. Gracias a todos ustedes

+0

No debe publicar una Responda SO, como si publicara una respuesta sobre un tema en un foro. Solo haz esto si respondes tu propia pregunta. Por lo general, agrega * ACTUALIZACIÓN * a su pregunta original como un tipo de respuesta a las respuestas y comentarios sobre su pregunta. –

2

Usted también podría agrupar por cierto - 1 (que en realidad está incluido cualquiera de los artículos y luego tener que contar o 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) 
            }; 

En cuanto a tiempo de encendido, se casi tan bueno como el bucle (con una adición constante).

+0

El 'group by' es completamente inútil y bastante confuso. Simplemente haga 'var results = new {... = ArticleLedgerEntries.Sum (...), ...}' – SLaks