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
更新:すべてのフィールドのグループ化を避けたい場合は、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
に同じName
とCategory
が適用されると想定しています。その場合、@ Aronが示唆するように正規化を検討する必要があります。 Id
とValue
を1つのクラスに保持し、Name
、Category
(および同じId
)を別のクラスに追加すると同時に、参照用にId
を使用します。正規化プロセスにより、データの冗長性と依存性が軽減されます。
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;}
}
これを試して:
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();
クラスが本当に長く、すべてのものをコピーしたくない場合は、次のようなものを試すことができます。
_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