GroupByのグルーピング対象を動的に変更する

.NET LinqでGroupByを使用して、クエリ内容をグルーピングする際に、場合によって、グルーピング対象を動的に変更したい場合がある。

これを実装するのに一番簡単な方法を見つけたのでメモ。

Enumerable.GroupBy拡張メソッドのオーバーロードの1つに、以下の物がある。

GroupBy<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>, IEqualityComparer<TKey>)

このオーバーロードのパラメータ、IEqualityComparer<TKey>をインプリメントしたクラスを作ることで、グルーピング単位を外部から指定できるようになる。

例えば、以下のようなクラスのリストを「年」+「月」でグルーピングしたり、「年」+「月」+「セールスマン」でグルーピングしたりしたい場合、

public partial class Sales
{
    public int Year { get; set; }            // 年
    public int Month { get; set; }           // 月
    public string SalesMan { get; set; }     // セールスマン
    public string ProductName { get; set; }  // 商品名
    public long Qty { get; set; }            // 販売数
}

IEqualityComparer<Sales>をインプリメントして、以下のようなクラスを作る。

class SalesEqaulityComparer : IEqualityComparer<Sales> {
    public bool useYear { get; set; }
    public bool useMonth { get; set; }
    public bool useSalesMan { get; set; }
    public bool useProductName { get; set; }
    // 比較方法(IEqualityComparer<T>)
    public bool Equals(Sales x, Sales y) {
        if (useYear && !x.Year.Equals(y.Year)) return false;
        if (useMonth && !x.Month.Equals(y.Month)) return false;
        if (useSalesMan && !x.SalesMan.Equals(y.SalesMan)) return false;
        if (useProductName && !x.ProductName.Equals(y.ProducName)) return false;
        return true;
    }
    // HashCodeの取得(IEqualityComparer<T>)
    public int GetHashCode(Sales s) {
        int hash = 0;
        if (useYear) hash ^= s.Year.GetHashCode();
        if (useMonth) hash ^= s.Month.GetHashCode();
        if (useSalesMan) hash ^= s.SalesMan.GetHashCode();
        if (useProductName) hash ^= s.ProductName.GetHashCode();
        return hash;
    }
}

実際にグルーピングするときは、このクラスインスタンスを作成し、グルーピングしたいuseXXXにtrueを設定することにより、指定した項目でグルーピングができる。

例えば、「年」,「月」でグルーピングしたい場合は、useYearとuseMonthにtrueを設定し、その他はfalseに設定。「年」,「月」,「セールスマン」でグルーピングしたい場合は、useYear,useMonth,useSalesManにtrueを設定して、その他はfalseを設定して、以下のようなクエリを発行する。

List<Sales> SalesList = new SalesList();
・・・
var salescomparer = new SalesEqaulityComparer();
// グルーピングしたい項目の指定
salescompare.useYear = true;
salescompare.useMonth = true;
・・・
var g = SalesList.GroupBy(s=>s,salescomparer);
foreach(var itm in g) {
	Console.WriteLine($"{itm.First().Year},{itm.First().Month},・・・{itm.Sum(v=>v.Qty)}}");
}

意外と使えます。

takezou について

ソフトウェア開発会社(ITと言う言葉は大嫌い)で働く、元技術者。 未だに、社内システム位は作ってますが・・・ プログラミング言語はC#が好き。 好きなことだけ拾って投稿しているので、内容にはあまり期待しないでねw
カテゴリー: .NET, C#, LINQ, 技術系 パーマリンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください