web-dev-qa-db-fra.com

Obtenez la somme de deux colonnes dans une requête LINQ

disons que j'ai une table appelée Items (ID int, Done int, Total int)

Je peux le faire par deux requêtes:

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

Mais je voudrais le faire en une seule requête, quelque chose comme ceci:

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

Il existe sûrement un moyen d'appeler des fonctions d'agrégation à partir de la syntaxe LINQ ...?

60
Axarydax

Ça fera l'affaire:

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

Pour résumer le tableau, groupez par une 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)
}
10
WhoIsNinja

Que diriez-vous

   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
Jens

Trouver où extraire les sommes ou autres agrégats dans le reste de mon code m'a dérouté, jusqu'à ce que je me souvienne que la variable que j'ai construite était un Iqueryable. Supposons que nous ayons un tableau dans notre base de données composé d'ordres et que nous voulons produire un résumé pour la société 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) )
};

Maintenant, la partie amusante - tempPrice et tempQty ne sont déclarées nulle part mais elles doivent faire partie de myResult, non? Accédez-y comme suit:

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

Un certain nombre d'autres méthodes Queryable pourraient également être utilisées.

6
viejo

Avec une classe Tuple d'assistance, la vôtre ou, dans .NET 4, les classes standard, vous pouvez le faire:

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

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

Et res.Item1 est le total de la colonne Total et res.Item2 de la colonne Done.

3
Richard
//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)
1
Gaushick

Cela a déjà été répondu, mais les autres réponses feront toujours plusieurs itérations sur la collection (plusieurs appels à Sum) ou créeront beaucoup d'objets/Tuples intermédiaires qui peuvent être corrects, mais si ce n'est pas le cas, vous pouvez créer une extension (ou multiple) qui le fait à l'ancienne mais s'intègre bien dans une expression LINQ.

Une telle méthode d'extension ressemblerait à ceci:

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

Et vous pouvez l'utiliser comme ceci:

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);
1
Steven Evers

En utilisant le support du langage pour les tuples introduit dans C # 7. vous pouvez résoudre ce problème en utilisant l'expression LINQ suivante:

var itemSums = m.Items.Aggregate((Total: 0, Done: 0), (sums, item) => (sums.Total + item.Total, sums.Done + item.Done));

Exemple de code complet:

var m = new
{
    Items = new[]
    {
        new { Total = 10, Done = 1 },
        new { Total = 10, Done = 1 },
        new { Total = 10, Done = 1 },
        new { Total = 10, Done = 1 },
        new { Total = 10, Done = 1 },
    },
};

var itemSums = m.Items.Aggregate((Total: 0, Done: 0), (sums, item) => (sums.Total + item.Total, sums.Done + item.Done));

Console.WriteLine($"Sum of Total: {itemSums.Total}, Sum of Done: {itemSums.Done}");
0
jfiskvik