自作クラスのリストを複数列でGroup byして集計する方法


例として、衣類を管理するアパレルクラスなるものを用意しました。

販売店、アイテム名、サイズ、価格、在庫数を持っているクラスです。
まぁ、わかりやすくするために項目は少なめです(笑)

アパレルクラス

// アパレルクラス
private class APPAREL
{
    public APPAREL()
    { 
    }
    public APPAREL(string shop, string itemName, Sizes size, int stock, int price)
    {
        this.Shop = shop;
        this.ItemName = itemName;
        this.Size = size;
        this.StockCount = stock;
        this.Price = price;
    }

    // 店舗名
    public string Shop { get; set; }
    // アイテム名
    public string ItemName { get; set; }
    // サイズ
    public Sizes Size { get; set; }
    // 在庫数
    public int StockCount { get; set; }
    // 販売価格
    public int Price { get; set; }

    // Size
    public enum Sizes
    { 
        XXS = 0,
        XS,
        S,
        M,
        L,
        XL,
        XXL
    }

    /// <summary>
    /// GroupByメソッド
    /// 全店舗のアイテム・サイズ毎の在庫数、販売価格を取得するメソッド
    /// </summary>
    /// <param name="list">
    /// Group byするリスト
    /// </param>
    /// <returns>
    /// Group byしたリスト
    /// </returns>
    public static List<APPAREL> GroupBy(List<APPAREL> list)
    {
        List<APPAREL> gbyList = (from a in list
                                 orderby a.ItemName,a.Size
                                 group a by new { a.ItemName, a.Size } into g
                                 select new APPAREL
                             {
                                 Shop = string.Empty,                 // ショップ名はgroup対象外なのでEmpty
                                 ItemName = g.Max(s => s.ItemName),   // 文字列でもMaxでOK
                                 Size = g.Max(s => s.Size),           // 列挙体でもMaxでOK 
                                 StockCount = g.Sum(s => s.StockCount),
                                 Price = g.Sum(s => s.Price)
                             }).ToList();

        return gbyList;
    }
}

このクラスのGroupByメソッドにリストを引数として渡すと
アイテム名とサイズでGroupByを行いリストで返却するように作ってあります。

「group a by new { a.ItemName, a.Size }」の「 a.ItemName, a.Size 」の箇所に
グループ化したい項目を記述していく感じになります。

実際にサンプルデータを作成してメソッドを実行した場合の処理と
返却結果をJsonに変換した場合の結果が下記です。

static void Main(string[] args)
{
    List<APPAREL> appList = new List<APPAREL>();
    APPAREL re = new APPAREL();

    //適当にテストデータを追加します
    appList.Add(new APPAREL("周防大島", "シャツ", APPAREL.Sizes.S, 2, 4500));
    appList.Add(new APPAREL("橘", "シャツ", APPAREL.Sizes.S, 3, 4500));
    appList.Add(new APPAREL("久賀", "シャツ", APPAREL.Sizes.M, 4, 5000));
    appList.Add(new APPAREL("長崎", "シャツ", APPAREL.Sizes.S, 5, 4500));
    appList.Add(new APPAREL("下田", "シャツ", APPAREL.Sizes.L, 6, 3820));
    appList.Add(new APPAREL("神浦", "シャツ", APPAREL.Sizes.S, 7, 2980));

    appList.Add(new APPAREL("周防大島", "ジャケット", APPAREL.Sizes.M, 2, 14500));
    appList.Add(new APPAREL("橘", "ジャケット", APPAREL.Sizes.XXL, 3, 10500));
    appList.Add(new APPAREL("久賀", "ジャケット", APPAREL.Sizes.XXS, 4, 10800));
    appList.Add(new APPAREL("長崎", "ジャケット", APPAREL.Sizes.S, 5, 4500));
    appList.Add(new APPAREL("下田", "ジャケット", APPAREL.Sizes.XXL, 6, 14500));
    appList.Add(new APPAREL("神浦", "ジャケット", APPAREL.Sizes.S, 7, 12000));

    // グループ化!
    List<APPAREL> liAPPAREL = APPAREL.GroupBy(appList);

    // 余談「JsonConvert.SerializeObject」の第二引数に「Formatting.Indented」を指定すると
    // Json文字列が整形される
    string json = JsonConvert.SerializeObject(liAPPAREL, Formatting.Indented);
    Console.WriteLine(json);

    /* 出力結果 (わかりやすいようにJsonにシリアライズしています)
    [
      {
        "Shop": "",
        "ItemName": "ジャケット",
        "Size": 0,
        "StockCount": 4,
        "Price": 10800
      },
      {
        "Shop": "",
        "ItemName": "ジャケット",
        "Size": 2,
        "StockCount": 12,
        "Price": 16500
      },
      {
        "Shop": "",
        "ItemName": "ジャケット",
        "Size": 3,
        "StockCount": 2,
        "Price": 14500
      },
      {
        "Shop": "",
        "ItemName": "ジャケット",
        "Size": 6,
        "StockCount": 9,
        "Price": 25000
      },
      {
        "Shop": "",
        "ItemName": "シャツ",
        "Size": 2,
        "StockCount": 17,
        "Price": 16480
      },
      {
        "Shop": "",
        "ItemName": "シャツ",
        "Size": 3,
        "StockCount": 4,
        "Price": 5000
      },
      {
        "Shop": "",
        "ItemName": "シャツ",
        "Size": 4,
        "StockCount": 6,
        "Price": 3820
      }
    ]
    */
}

使いドコロはまぁそれなりに限られますが、よく使うGroupByだけ実装しておけば便利かも。