2010-03-12 19 views
48

diciamo che ho una tabella chiamata Items (ID int, int Fatto, int totale)Ottenere somma di due colonne in una query LINQ

posso farlo da due query:

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

Ma mi piacerebbe farlo in una query, qualcosa di simile:

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

Sicuramente c'è un modo per chiamare funzioni di aggregazione dalla sintassi LINQ ...?

risposta

68

Questo farà il trucco:

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 quando elemento non ha identificatore univoco, è possibile scrivere 'gruppo p da p in g'. – Steven

+1

Questo ha fatto il trucco, anche se con una modifica: da p in m.Items gruppo p di p.Id in g selezionare new {SumTotal = g.Sum (r => r.Total), SumDone = g.Sum (r => r.Done)} – Axarydax

+1

Hai ragione, 'p' era già utilizzato nella query. Aggiustato. – Steven

1

Con una classe di supporto tuple, il tuo o — in .NET 4 — quelli standard si può fare questo:

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

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

E res.Item1 è il totale della colonna Total e res.Item2 della colonna Done .

8

Come su

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

Per riassumere la tavola, gruppo da una costante:

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

per favore qualcuno può pubblicare una versione di vb.net? – Zeus

+0

@Zeus Ecco il codice VB.Net, da g In da p In m.ItemsGroup p Di 1New con {_ \t Chiave .SumTotale = g.Sum (Funzione x) Totale), _ \t Chiave. SumDone = g.Sum (Function (x) x.Done) _ } Puoi convertire i codici qui [link] (http://converter.telerik.com/) –

+0

@Sibeesh, grazie per l'aiuto – Zeus

5

Capire dove estrarre le somme o altro aggregato nel resto del mio codice mi ha confuso, fino a quando ho ricordato che la variabile che ho costruito era un Iqueryable. Supponiamo di avere un tavolo nel nostro database composto da Ordini, e vogliamo produrre una sintesi per l'azienda 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)) 
}; 

Ora la parte divertente - tempPrice e tempQty non vengono dichiarati da nessuna parte ma devono essere parte di myResult, no? Accedervi come segue:

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

È possibile utilizzare anche un numero di altri metodi interrogabili.

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

Quando si utilizza gruppo da LINQ crea una nuova collezione di oggetti in modo da avere due collezioni di oggetti.

Ecco una soluzione a entrambi i problemi:

  1. sommando qualsiasi quantità di membri in un'iterazione e
  2. evitare la duplicazione di raccolta del tuo articolo

Codice:

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

Ecco il modo in cui devi fare la sommatoria:

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

Ecco un esempio generale:

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

come si può vedere

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

Questo è stato risposto già, ma le altre risposte saranno ancora fare più iterazioni sulla raccolta (più chiamate per sommare) o creare molti oggetti/tuple intermedi che possono andare bene, ma se non lo sono, è possibile creare un metodo di estensione (o multiplo) che fa la vecchia maniera ma si adatta bene a un'espressione LINQ.

Tale metodo di estensione sarebbe simile a questa:

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

e si può usare in questo modo:

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