2010-03-12 16 views
48

digamos que tengo una tabla llamada artículos (int Id, int Hecho, int total)Obtener suma de dos columnas en una consulta LINQ

puedo hacerlo por dos consultas:

int total = m.Items.Sum(p=>p.Total) 
int done = m.Items.Sum(p=>p.Done) 

Pero me gustaría hacerlo en una consulta, algo como esto:

var x = from p in m.Items select new { Sum(p.Total), Sum(p.Done)}; 

Sin duda, hay una forma de llamar a las funciones de agregación de la sintaxis de LINQ ...?

Respuesta

68

Esto va a hacer el truco:

from p in m.Items 
group p by 1 into g 
select new 
{ 
    SumTotal = g.Sum(x => x.Total), 
    SumDone = g.Sum(x => x.Done) 
}; 
+7

O cuando artículo no tiene identificador único, se podría escribir 'grupo p por p en g'. – Steven

+1

Eso hizo el truco, aunque con una modificación: de p en m.Items grupo p por p.Id en g seleccione nuevo {SumTotal = g.Sum (r => r.Total), SumDone = g.Sum (r => r.Done)} – Axarydax

+1

Tiene razón, 'p' ya se utilizó en la consulta. Arreglado. – Steven

1

Con una clase de ayuda tupla, ya sea solo o en .NET las estándar se puede hacer esto:

var init = Tuple.Create(0, 0); 

var res = m.Items.Aggregate(init, (t,v) => Tuple.Create(t.Item1 + v.Total, t.Item2 + v.Done)); 

Y res.Item1 es el total de la columna de la Total y res.Item2 de la columna de Done .

8

¿Qué tal

m.Items.Select(item => new { Total = item.Total, Done = item.Done }) 
      .Aggregate((t1, t2) => new { Total = t1.Total + t2.Total, Done = t1.Done + t2.Done }); 
9

Para resumir la tabla, grupo por una constante:

from p in m.Items 
group p by 1 into g 
select new { 
    SumTotal = g.Sum(x => x.Total), 
    SumDone = g.Sum(x => x.Done) 
} 
+0

por favor alguien puede publicar una versión vb.net? – Zeus

+0

@Zeus Aquí está el código VB.Net, desde g In From p In m.ItemsGroup p Por 1New Con {_ \t Key .SumTotal = g.Sum (Function (x) x.Total), _ \t Key. SumDone = g.Sum (Function (x) x.Done) _ } Puede convertir los códigos aquí [link] (http://converter.telerik.com/) –

+0

@Sibeesh, gracias por la ayuda – Zeus

5

Averiguar dónde extraer las sumas u otro agregado en el resto de mi código me confundió, hasta que recordé que la variable que construí era ilegible. Supongamos que tenemos una tabla en nuestra base de datos compuesta de Órdenes, y queremos producir un resumen para la empresa ABC:

var myResult = from g in dbcontext.Ordertable 
       group p by (p.CUSTNAME == "ABC") into q // i.e., all of ABC company at once 
       select new 
{ 
    tempPrice = q.Sum(x => (x.PRICE ?? 0m)), // (?? makes sure we don't get back a nullable) 
    tempQty = q.Sum(x => (x.QTY ?? 0m)) 
}; 

Ahora la parte divertida - tempPrice y tempQty no son declarados en cualquier lugar, sino que deben ser parte de myResult, no? Acceda a ellos de la siguiente manera:

Console.Writeline(string.Format("You ordered {0} for a total price of {1:C}", 
           myResult.Single().tempQty, 
           myResult.Single().tempPrice)); 

También se pueden usar otros métodos query.

1
//Calculate the total in list field values 
//Use the header file: 

Using System.Linq; 
int i = Total.Sum(G => G.First); 

//By using LINQ to calculate the total in a list field, 

var T = (from t in Total group t by Total into g select g.Sum(t => t.First)).ToList(); 

//Here Total is a List and First is the one of the integer field in list(Total) 
-2

Cuando se utiliza grupo por LINQ crea una nueva colección de artículos por lo que tiene dos colecciones de artículos.

he aquí una solución a ambos problemas:

  1. sumando cualquier cantidad de miembros en una iteración y
  2. evitar la duplicación de la colección de su elemento

Código:

public static class LinqExtensions 
{ 
    /// <summary> 
    /// Computes the sum of the sequence of System.Double values that are obtained 
    /// by invoking one or more transform functions on each element of the input sequence. 
    /// </summary> 
    /// <param name="source">A sequence of values that are used to calculate a sum.</param> 
    /// <param name="selectors">The transform functions to apply to each element.</param>  
    public static double[] SumMany<TSource>(this IEnumerable<TSource> source, params Func<TSource, double>[] selectors) 
    { 
    if (selectors.Length == 0) 
    { 
     return null; 
    } 
    else 
    { 
     double[] result = new double[selectors.Length]; 

     foreach (var item in source) 
     { 
     for (int i = 0; i < selectors.Length; i++) 
     { 
      result[i] += selectors[i](item); 
     } 
     } 

     return result; 
    } 
    } 

    /// <summary> 
    /// Computes the sum of the sequence of System.Decimal values that are obtained 
    /// by invoking one or more transform functions on each element of the input sequence. 
    /// </summary> 
    /// <param name="source">A sequence of values that are used to calculate a sum.</param> 
    /// <param name="selectors">The transform functions to apply to each element.</param> 
    public static double?[] SumMany<TSource>(this IEnumerable<TSource> source, params Func<TSource, double?>[] selectors) 
    { 
    if (selectors.Length == 0) 
    { 
     return null; 
    } 
    else 
    { 
     double?[] result = new double?[selectors.Length]; 

     for (int i = 0; i < selectors.Length; i++) 
     { 
     result[i] = 0; 
     } 

     foreach (var item in source) 
     { 
     for (int i = 0; i < selectors.Length; i++) 
     { 
      double? value = selectors[i](item); 

      if (value != null) 
      { 
      result[i] += value; 
      } 
     } 
     } 

     return result; 
    } 
    } 
} 

Aquí está la forma en que tiene que hacer la sumatoria:

double[] result = m.Items.SumMany(p => p.Total, q => q.Done); 

Aquí está un ejemplo general:

struct MyStruct 
{ 
    public double x; 
    public double y; 
} 

MyStruct[] ms = new MyStruct[2]; 

ms[0] = new MyStruct() { x = 3, y = 5 }; 
ms[1] = new MyStruct() { x = 4, y = 6 }; 

// sum both x and y members in one iteration without duplicating the array "ms" by GROUPing it 
double[] result = ms.SumMany(a => a.x, b => b.y); 

como se puede ver

result[0] = 7 
result[1] = 11 
1

Esto ha sido contestada ya, pero las otras respuestas todavía va a hacer múltiples iteraciones más de la colección (múltiples llamadas Suma) o crear muchos objetos intermedios/Tuplas que pueden estar bien, pero si no es así, entonces puede crear un método de extensión (o múltiple) que lo haga a la antigua usanza pero que se ajuste bien a una expresión LINQ.

Tal método de extensión se vería así:

public static Tuple<int, int> Sum<T>(this IEnumerable<T> collection, Func<T, int> selector1, Func<T, int> selector2) 
{ 
    int a = 0; 
    int b = 0; 

    foreach(var i in collection) 
    { 
     a += selector1(i); 
     b += selector2(i); 
    } 

    return Tuple.Create(a, b); 
} 

Y usted puede utilizar de esta manera:

public class Stuff 
{ 
    public int X; 
    public int Y; 
} 

//... 

var stuffs = new List<Stuff>() 
{ 
    new Stuff { X = 1, Y = 10 }, 
    new Stuff { X = 1, Y = 10 } 
}; 

var sums = stuffs.Sum(s => s.X, s => s.Y); 
Cuestiones relacionadas