potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C# Enumerable.ToLookup(LINQ)で配列やリスト(IEnumerable)を条件ごとに分ける

C#ではEnumerable.ToLookupLINQ)を使うことで配列やリスト(IEnumerable)を条件ごとに分類できます。ToDictionaryと組み合わせることで辞書も作成できます。

模式図:
Enumerable.ToLookup:■□■□○ → ■■、□□、○
■□○:分類された要素(キーセレクタが同じキーを返す要素)

Enumerable.ToLookupの例

Enumerable.ToLookupのサンプルコードです。

整数配列を3で割った余りで分類する。

C# 9(トップレベステートメント

using System.Linq;

var intArray1 = new[] { 0, 1, 2, 3, 4, 5, 6 };
var lookup1 = intArray1.ToLookup(
        i => i % 3, // keySelector
        i => i);    // valueSelector
                    // lookup1[0]: {0, 3, 6}
                    // lookup1[1]: {1, 4}
                    // lookup1[2]: {2, 5}
var item0 = lookup1[0].ToArray();
// {0, 3, 6}

C# 8

using System.Linq;

class Program
{
    static void Main()
    {
        var intArray1 = new[] { 0, 1, 2, 3, 4, 5, 6 };
        var lookup1 = intArray1.ToLookup(
                i => i % 3, // keySelector
                i => i);    // valueSelector
        // lookup1[0]: {0, 3, 6}
        // lookup1[1]: {1, 4}
        // lookup1[2]: {2, 5}
        var item0 = lookup1[0].ToArray();
        // {0, 3, 6}
    }
}

文字列配列を最後の文字の大文字で分類して末尾に'0'をつける。

C# 9(トップレベステートメント

using System.Linq;

var strArray1 = new[] { "abc", "def", "gfc", "aaa", "ccc" };
var lookup1 = strArray1.ToLookup(
        s => char.ToUpper(s.TakeLast(1).ElementAt(0)),
        s => s + '0');
// lookup1['C']: {"abc0", "gfc0", "ccc0"}
// lookup1['F']: {"def0"}
// lookup1['A']: {"aaa0"}
var itemc = lookup1['c'].ToArray();
// {}
var itemC = lookup1['C'].ToArray();
// {"abc0", "gfc0", "ccc0"}

strArray1[0] = "ppppp";
// lookup1の内容は変わりません。

C# 8

using System.Linq;

class Program
{
    static void Main()
    {
        var strArray1 = new[] { "abc", "def", "gfc", "aaa", "ccc" };
        var lookup1 = strArray1.ToLookup(
                s => char.ToUpper(s.TakeLast(1).ElementAt(0)),
                s => s + '0');
        // lookup1['C']: {"abc0", "gfc0", "ccc0"}
        // lookup1['F']: {"def0"}
        // lookup1['A']: {"aaa0"}
        var itemc = lookup1['c'].ToArray();
        // {}
        var itemC = lookup1['C'].ToArray();
        // {"abc0", "gfc0", "ccc0"}

        strArray1[0] = "ppppp";
        // lookup1の内容は変わりません。
    }
}

文字列配列を末尾が'c'かどうかで分類して先頭の2文字にする。

C# 9(トップレベステートメント

using System.Linq;

var strArray1 = new[] { "abc", "def", "gfc", "aaa", "ccc" };
var lookup1 = strArray1.ToLookup(
        key => key.EndsWith('c'),
        el => el.Substring(0, 2));
// lookup1[true]: {"ab", "gf", "cc"}
// lookup1[false]: {"de", "aa"}
var item0 = lookup1[false].ToArray();
// {"de", "aa"}

C# 8

using System.Linq;

class Program
{
    static void Main()
    {
        var strArray1 = new[] { "abc", "def", "gfc", "aaa", "ccc" };
        var lookup1 = strArray1.ToLookup(
                key => key.EndsWith('c'),
                el => el.Substring(0, 2));
        // lookup1[true]: {"ab", "gf", "cc"}
        // lookup1[false]: {"de", "aa"}
        var item0 = lookup1[false].ToArray();
        // {"de", "aa"}
    }
}

Enumerable.ToLookupとEnumerable.ToDictionaryの組み合わせ

Enumerable.ToLookupで得られるILookupEnumerable.ToDictionaryを適用して配列やリストの辞書を作成できます。ILookup.ToDictionaryIGroupingが渡されるので、IGrouping.Keyでキー、IGrouping.ToArrayIGrouping.ToListで配列やリストの値を指定しています。

C# 9(トップレベステートメント

using System.Linq;

var intArray1 = new[] { 0, 1, 2, 3, 4, 5, 6 };

// 値が配列の辞書を作成する。
var dict1 = intArray1.ToLookup(
        key => key % 3,
        el => el)
    .ToDictionary(key => key.Key, el => el.ToArray());
// [0]: int[3] {0, 3, 6}
// [1]: int[2] {1, 4}
// [2]: int[2] {2, 5}

// 値がリストの辞書を作成する。
var dict2 = intArray1.ToLookup(
        key => key % 3,
        el => el)
    .ToDictionary(key => key.Key, el => el.ToList());
// [0]: List<int> {0, 3, 6}
// [1]: List<int> {1, 4}
// [2]: List<int> {2, 5}

C# 8

using System.Linq;

class Program
{
    static void Main()
    {
        var intArray1 = new[] { 0, 1, 2, 3, 4, 5, 6 };

        // 値が配列の辞書を作成する。
        var dict1 = intArray1.ToLookup(
                key => key % 3,
                el => el)
            .ToDictionary(key => key.Key, el => el.ToArray());
        // [0]: int[3] {0, 3, 6}
        // [1]: int[2] {1, 4}
        // [2]: int[2] {2, 5}

        // 値がリストの辞書を作成する。
        var dict2 = intArray1.ToLookup(
                key => key % 3,
                el => el)
            .ToDictionary(key => key.Key, el => el.ToList());
        // [0]: List<int> {0, 3, 6}
        // [1]: List<int> {1, 4}
        // [2]: List<int> {2, 5}
    }
}