[C# LINQ] SelectManyでリストの中のリストを1つにまとめる

LINQのSelectManyメソッドを使うと、リストの中にあるリストを1つにまとめることができます(平坦化というらしい)。配列にも使えます。
言葉ではわかりにくいのでさっそくサンプルです。

スポンサーリンク

リストの中のリストを1つにまとめる

SelectManyメソッドの引数にはまとめたいリストや配列を返すメソッドを指定します。

「まとめたいリストや配列を返すメソッド」の引数にはリストの各要素が渡されます。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    public static void Main()
    {
        // SampleDataクラスのリストの宣言と初期化
        List<SampleData> list = new List<SampleData> {
            new SampleData { Name = "a", DataList = new List<int> { 1, 2, 3 } },
            new SampleData { Name = "b", DataList = new List<int> { 4, 5, 6 } },
            new SampleData { Name = "c", DataList = new List<int> { 7, 8, 9 } },
        };

        // 元のリスト
        Console.WriteLine("元のリスト");
        foreach (var data in list) Console.WriteLine($"Name={data.Name}, DataList=[{string.Join(", ", data.DataList)}]");

        // SelectManyでリストの中のリストを1つにまとめる
        IEnumerable<int> result1 = list.SelectMany(data => data.DataList);
        Console.WriteLine("\nSelectManyでリストの中のリストを1つにまとめる");
        Console.WriteLine($"{string.Join(", ", result1)}");
    }
}

class SampleData
{
    public string Name { get; set; }
    public List<int> DataList { get; set; }
}

SelectManyメソッドを使ってまとめているのは、サンプルコード20行目です。
引数にはまとめたいリスト(SampleDataクラスのDataList)を返すメソッドを指定しています。
出力結果はこんな感じ。

元のリスト
Name=a, DataList=[1, 2, 3]
Name=b, DataList=[4, 5, 6]
Name=c, DataList=[7, 8, 9]

SelectManyでリストの中のリストを1つにまとめる
1, 2, 3, 4, 5, 6, 7, 8, 9

同時にインデックスも取得する

Selectメソッドと同じようにSelectManyメソッドでも各要素のIndexを取得することができます。Selectメソッドについては、[C# LINQ] Selectメソッドで各要素から必要な項目を取り出すを見てください。

外側のリストのインデックス、内側のリストのインデックス両方が取得できます。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    public static void Main()
    {
        // SampleDataクラスのリストの宣言と初期化
        List<SampleData> list = new List<SampleData> {
            new SampleData { Name = "a", DataList = new List<int> { 1, 2, 3 } },
            new SampleData { Name = "b", DataList = new List<int> { 4, 5, 6 } },
            new SampleData { Name = "c", DataList = new List<int> { 7, 8, 9 } },
        };

        // SelectManyでまとめる際にインデックスも取得する
        // dataには外側のリストの各要素(SampleData)が渡される
        // valueには内側のリスト(DataList)の各要素(int型の値)が渡される
        var result2 = list.SelectMany(
            (data, outIndex) => data.DataList.Select(
                (value, innerIndex) => new { OutIndex = outIndex, InnerIndex = innerIndex, Value = value }
            )
        );
        foreach (var ret in result2)
        {
            Console.WriteLine($"OutIndex={ret.OutIndex}, InnerIndex={ret.InnerIndex}, Value={ret.Value}");
        }
    }
}

class SampleData
{
    public string Name { get; set; }
    public List<int> DataList { get; set; }
}
OutIndex=0, InnerIndex=0, Value=1
OutIndex=0, InnerIndex=1, Value=2
OutIndex=0, InnerIndex=2, Value=3
OutIndex=1, InnerIndex=0, Value=4
OutIndex=1, InnerIndex=1, Value=5
OutIndex=1, InnerIndex=2, Value=6
OutIndex=2, InnerIndex=0, Value=7
OutIndex=2, InnerIndex=1, Value=8
OutIndex=2, InnerIndex=2, Value=9
スポンサーリンク

まとめたものと他の項目と組み合わせる

リストの中のリストを1つにまとめるときに他の項目と組み合わせることが出来ます。
SelectManyメソッドの第1引数にまとめたいリストや配列を返すメソッド
第2引数に外側のリストの各要素とまとめた値1つ1つを使って新しい型を返すメソッドを指定します。

自分でもなにを言ってるかさっぱりなのでサンプルコードをどうぞ。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    public static void Main()
    {
        // SampleDataクラスのリストの宣言と初期化
        List<SampleData> list = new List<SampleData> {
            new SampleData { Name = "a", DataList = new List<int> { 1, 2, 3 } },
            new SampleData { Name = "b", DataList = new List<int> { 4, 5, 6 } },
            new SampleData { Name = "c", DataList = new List<int> { 7, 8, 9 } },
        };

        // SelectManyでリストの中のリストを1つにまとめたものを他の項目と組み合わせる
        var result = list.SelectMany(
            data => data.DataList,
            (data, DataList_value) => new { Name = data.Name, Value = DataList_value }
        );
        // 結果を表示
        foreach (var d in result)
        {
            Console.WriteLine($"Name={d.Name}, Value={d.Value}");
        }
    }
}

class SampleData
{
    public string Name { get; set; }
    public List<int> DataList { get; set; }
}

こんな感じにまとめることが出来ます。

Name=a, Value=1
Name=a, Value=2
Name=a, Value=3
Name=b, Value=4
Name=b, Value=5
Name=b, Value=6
Name=c, Value=7
Name=c, Value=8
Name=c, Value=9

まとめたものと他の項目と組み合わせつつ、ついでに各インデックスを取得する

嫌がらせのようにタイトルが長いですが、こちらも同時にインデックスを取得することができます。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    public static void Main()
    {
        // SampleDataクラスのリストの宣言と初期化
        List<SampleData> list = new List<SampleData> {
            new SampleData { Name = "a", DataList = new List<int> { 1, 2, 3 } },
            new SampleData { Name = "b", DataList = new List<int> { 4, 5, 6 } },
            new SampleData { Name = "c", DataList = new List<int> { 7, 8, 9 } },
        };

        // SelectManyでリストの中のリストを1つにまとめたものを他の項目と組み合わせつつインデックスを取得する
        var result = list.SelectMany(
            (data, outIndex) => data.DataList.Select(
                (value, innerIndex) => new { OutIndex = outIndex, InnerIndex = innerIndex, Value = value }
            ),
            (data, selectData) => new { selectData.OutIndex, selectData.InnerIndex, data.Name, selectData.Value }
        );
        // 結果を表示
        foreach (var d in result)
        {
            Console.WriteLine($"OutIndex={d.OutIndex}, InnerIndex={d.InnerIndex}, Name={d.Name}, Value={d.Value}");
        }
    }
}

class SampleData
{
    public string Name { get; set; }
    public List<int> DataList { get; set; }
}

こんな感じになります。

OutIndex=0, InnerIndex=0, Name=a, Value=1
OutIndex=0, InnerIndex=1, Name=a, Value=2
OutIndex=0, InnerIndex=2, Name=a, Value=3
OutIndex=1, InnerIndex=0, Name=b, Value=4
OutIndex=1, InnerIndex=1, Name=b, Value=5
OutIndex=1, InnerIndex=2, Name=b, Value=6
OutIndex=2, InnerIndex=0, Name=c, Value=7
OutIndex=2, InnerIndex=1, Name=c, Value=8
OutIndex=2, InnerIndex=2, Name=c, Value=9

C# プログラミング講座に戻る

コメント

タイトルとURLをコピーしました