xin9le.net

Microsoft の製品/技術が大好きな Microsoft MVP な管理人の技術ブログです。

1 次元配列から 2 次元辞書を作ろう

以前 2 次元配列の要素をインデックス付きで 1 次元配列に落とす、というのを紹介しました。

となると、その逆もやってみたくなりませんか…?*1

拡張メソッドを作る

通常の ToDictionary メソッドはひとつのプロパティをキーにして辞書を作りますが、このキーをふたつにすればよいでしょう。以下のような感じになります。

//--- キーだけ選択してそのまま要素を格納するバージョン
public static Dictionary<TKeyX, Dictionary<TKeyY, TSource>> ToDictionary2<TSource, TKeyX, TKeyY>
    (
        this IEnumerable<TSource> self,
        Func<TSource, TKeyX> xSelector,
        Func<TSource, TKeyY> ySelector
    )
{
    if (self == null)      throw new ArgumentNullException(nameof(self));
    if (xSelector == null) throw new ArgumentNullException(nameof(xSelector));
    if (ySelector == null) throw new ArgumentNullException(nameof(ySelector));

    return  self.GroupBy(xSelector)
            .ToDictionary(x => x.Key, xs => xs.ToDictionary(ySelector));
}

//--- キーを選択するだけでなく、要素として何を格納するかを選ぶバージョン
public static Dictionary<TKeyX, Dictionary<TKeyY, TElement>> ToDictionary2<TSource, TKeyX, TKeyY, TElement>
    (
        this IEnumerable<TSource> self,
        Func<TSource, TKeyX> xSelector,
        Func<TSource, TKeyY> ySelector,
        Func<TSource, TElement> elementSelector
    )
{
    if (self == null)            throw new ArgumentNullException(nameof(self));
    if (xSelector == null)       throw new ArgumentNullException(nameof(xSelector));
    if (ySelector == null)       throw new ArgumentNullException(nameof(ySelector));
    if (elementSelector == null) throw new ArgumentNullException(nameof(elementSelector));

    return  self.GroupBy(xSelector)
            .ToDictionary(x => x.Key, xs => xs.ToDictionary(ySelector, elementSelector));
}

使ってみよう

こんなものが一体どんなケースで役に立つというのだ…。と思うかもしれませんが、例えば以下のようなものがあります。

//--- こんなクラスがあるとする
class Block
{
    public X { get; set; }
    public Y { get; set; }
    public Color Color { get; set; }
}

//--- こういう 1 次元配列を 2 次元辞書化してしまえば...
var blocks = new []
{
    new Block{ X = 0, Y = 0, Color = Colors.Red },
    new Block{ X = 0, Y = 1, Color = Colors.Blue },
    new Block{ X = 1, Y = 0, Color = Colors.Green },
    new Block{ X = 1, Y = 1, Color = Colors.Yellow },
}
.ToDictionary2(x => x.X, x => Y);

//--- こんなに分かりやすくなる!
var color = blocks[0][1].Color;

…滅多に使わないとは思います(ハイ

*1:普通ならないと思う