web-dev-qa-db-ja.com

linq distinctまたは複数のプロパティによるグループ化

C#とLinqを使用して、次のリストからresultを取得するにはどうすればよいですか。

 var pr = new List<Product>()
   {
       new Product() {Title="Boots",Color="Red",    Price=1},
       new Product() {Title="Boots",Color="Green",  Price=1},
       new Product() {Title="Boots",Color="Black",  Price=2},

       new Product() {Title="Sword",Color="Gray", Price=2},
       new Product() {Title="Sword",Color="Green",Price=2}
   };

Result

        {Title="Boots",Color="Red",  Price=1},               
        {Title="Boots",Color="Black",  Price=2},             
        {Title="Sword",Color="Gray", Price=2}

GroupByまたはDistinctを使用する必要があることは知っていますが、必要なものを取得する方法を理解しています

   List<Product> result = pr.GroupBy(g => g.Title, g.Price).ToList(); //not working
   List<Product> result  = pr.Distinct(...);

助けてください

32
user3052475

必要なプロパティごとにグループ化し、以下を選択します。

List<Product> result = pr.GroupBy(g => new { g.Title, g.Price })
                         .Select(g => g.First())
                         .ToList();
81
Ilya

新しい匿名型は機能しますが、独自の型を作成するか、 Tuple を使用すると、メソッドの外部でより意味があり、読みやすく、使いやすくなります。 (それ以外の場合は、区切り文字列string.Format({0}.{1}, g.Title, g.Price)を使用するだけで十分な場合があります)

List<Product> result = pr.GroupBy(g => new Tuple<string, decimal>(g.Title, g.Price))
                     .ToList();

List<Product> result = pr.GroupBy(g => new ProductTitlePriceGroupKey(g.Title, g.Price))
                     .ToList();

必要な結果セットを取得することに関して、提供された答えは最初の結果を返すことを示唆しています。おそらくあなたの目的には問題ありませんが、理想的にはColorを集計または無視する手段を提供する必要があります。

例えば、おそらく含まれている色をリストした方がいいでしょう。

List<Product> result = pr
                     .GroupBy(g => new Tuple<string, decimal>(g.Title, g.Price))
                     .Select(x => new Product()
                             { 
                                  Title = x.Key.Item1, 
                                  Price = x.Key.Item2,
                                  Color = string.Join(", ", x.Value.Select(y => y.Color) // "Red, Green"
                             })
                     .ToList();

色の単純な文字列プロパティの場合、それらを単純に連結することは理にかなっています。そこに別のエンティティがある場合、または単にその情報を抽象化したくない場合は、おそらくそのエンティティタイプのコレクションを持つ別のエンティティをすべて用意するのが最善でしょう。たとえば、タイトルと色でグループ化する場合、平均価格または価格の範囲を表示したい場合があります。各グループの最初のものを選択するだけではできないようになります。

List<ProductGroup> result = pr
                     .GroupBy(g => new Tuple<string, decimal>(g.Title, g.Price))
                     .Select(x => new ProductGroup()
                             { 
                                  Title = x.Key.Item1, 
                                  Price = x.Key.Item2,
                                  Colors = x.Value.Select(y => y.Color)
                             })
                     .ToList();
3
JoeBrockhaus