web-dev-qa-db-ja.com

複数のフィールドをグループ化して選択し、合計する

Linq(オブジェクトのリスト)でクエリを実行したいのですが、どうすればいいのかわかりません。グループと合計はできますが、残りのフィールドは選択できません。例:

ID  Value     Name   Category
1   5         Name1  Category1  
1   7         Name1  Category1
2   1         Name2  Category2
3   6         Name3  Category3
3   2         Name3  Category3

IDでグループ化し、値でSUMし、このようなすべてのフィールドを返します。

ID  Value     Name   Category
1   12        Name1  Category1  
2   1         Name2  Category2
3   8         Name3  Category3
29
user2112420

更新:すべてのフィールドのグループ化を避けたい場合は、Idでグループ化できます。

data.GroupBy(d => d.Id)
    .Select(
        g => new
        {
            Key = g.Key,
            Value = g.Sum(s => s.Value),
            Name = g.First().Name,
            Category = g.First().Category 
        });

ただし、このコードでは、各Idに同じNameCategoryが適用されると想定しています。その場合、@ Aronが示唆するように正規化を検討する必要があります。 IdValueを1つのクラスに保持し、NameCategory(および同じId)を別のクラスに追加すると同時に、参照用にIdを使用します。正規化プロセスにより、データの冗長性と依存性が軽減されます。

67
Diana Ionita
void Main()
{
            //Me being lazy in init
    var foos = new []
    {
        new Foo { Id = 1, Value = 5},
        new Foo { Id = 1, Value = 7},
        new Foo { Id = 2, Value = 1},
        new Foo { Id = 3, Value = 6},
        new Foo { Id = 3, Value = 2},
    };
    foreach(var x in foos)
    {
        x.Name = "Name" + x.Id;
        x.Category = "Category" + x.Id;
    }
            //end init.

    var result = from x in foos
                group x.Value by new { x.Id, x.Name, x.Category}
                into g
                select new { g.Key.Id, g.Key.Name, g.Key.Category, Value = g.Sum()};
    Console.WriteLine(result);
}

// Define other methods and classes here
public class Foo
{
    public int Id {get;set;}
    public int Value {get;set;}

    public string Name {get;set;}
    public string Category {get;set;}   
}
3
Aron

これを試して:

var objList = new List<SampleObject>();

objList.Add(new SampleObject() { ID = 1, Value = 5, Name = "Name1", Category = "Catergory1"});
objList.Add(new SampleObject() { ID = 1, Value = 7, Name = "Name1", Category = "Catergory1"});
objList.Add(new SampleObject() { ID = 2, Value = 1, Name = "Name2", Category = "Catergory2"});
objList.Add(new SampleObject() { ID = 3, Value = 6, Name = "Name3", Category = "Catergory3"});
objList.Add(new SampleObject() { ID = 3, Value = 2, Name = "Name3", Category = "Catergory3"});

var newList = from val in objList
              group val by new { val.ID, val.Name, val.Category } into grouped
              select new SampleObject() { ID = grouped.ID, Value = grouped.Sum(), Name = grouped.Name, Category = grouped.Category };

lINQPadで確認するには:

newList.Dump();
1
lexeRoy

クラスが本当に長く、すべてのものをコピーしたくない場合は、次のようなものを試すことができます。

_l.GroupBy(x => x.id).
  Select(x => {
    var ret = x.First();
    ret.value = x.Sum(xt => xt.value);
    return ret;
  }).ToList();
_

大きな力には大きな責任が伴います。注意する必要があります。行ret.value = x.Sum(xt => xt.value)変更します新しいオブジェクトではなく、参照を渡しているため、元のコレクションです。避けたい場合は、CloneのようなMemberwiseCloneメソッドをクラスに追加する必要があります(ただし、これは浅いコピーを作成するので注意してください)。行を単にvar ret = x.First().Clone();に置き換えるAfer

0
titol