web-dev-qa-db-ja.com

1つのLINQクエリで2つの列の合計を取得する

アイテム(ID int、Done int、Total int)というテーブルがあるとしましょう。

2つのクエリで実行できます。

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

しかし、次のような1つのクエリでそれを行いたいと思います。

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

確かに、LINQ構文から集計関数を呼び出す方法はありますか?

60
Axarydax

これはトリックを行います:

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

テーブルを合計するには、定数でグループ化します。

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

どう?

   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

私が作成した変数がIqueryableであることを思い出すまで、コードの残りの部分で合計またはその他の集計を抽出する場所を把握することは私を混乱させました。データベースにOrderで構成されるテーブルがあり、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) )
};

楽しい部分-tempPriceとtempQtyはどこにも宣言されていませんが、myResultの一部である必要がありますか?次のようにアクセスします。

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

他の多くのQueryableメソッドも使用できます。

6
viejo

ヘルパーTupleクラスを使用すると、独自のクラス、または.NET 4では、これを行うことができる標準クラスを使用できます。

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

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

res.Item1は、Total列とDone列のres.Item2の合計です。

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

これはすでに回答済みですが、他の回答はコレクションに対して複数回の反復(Sumへの複数の呼び出し)を行うか、多数の中間オブジェクト/タプルを作成しますが、そうでない場合は、拡張機能を作成できます昔ながらの方法(または複数)を実行しますが、LINQ式にうまく適合します。

このような拡張メソッドは次のようになります。

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

そして、次のように使用できます。

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

C#7. で導入されたタプルの言語サポートを使用すると、次のLINQ式を使用してこれを解決できます。

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

完全なコードサンプル:

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