◆ Select して 1 段フラット化
◆ コールバック関数の返り値はリストになってないといけない 

たまに見かけてなんだっけってググる SelectMany
未だに覚えられません

ブログにまとめればちょっとは記憶に残るかなってことで書いておきます

Select

名前の通り SelectMany メソッドは Select に関係あります
Select は JavaScript の map の動きです

SQL の SELECT から来ていて リストのそれぞれの要素に対して何らかの処理をしてその結果のリストを返します
> new List<int> { 1, 2, 3 }.Select(e => e * 10).ToList()
List<int>(3) { 10, 20, 30 }

SelectMany

SelectMany になると Select と同じようにリストの要素それぞれを引数に変換する関数を指定しますが 返り値はリストになっていないといけないです

それぞれの要素がリストになっていて 2 次元配列のようになっているものを 1 段フラットにしたリストとして返すのが SelectMany です
フラットにする処理があるので結果のリストの要素数は元のリストとは異なる場合があります

サンプル

a,b
c
d,e,f
という csv データを [a,b,c,d,e,f] にしたいとします

最初の改行の Split は省略して直接書いてます
> new List<string> { "a,b", "c", "d,e,f" }.SelectMany(e => e.Split(',')).ToList()
List<string>(6) { "a", "b", "c", "d", "e", "f" }

Select を使う場合はこうなります
> new List<string> { "a,b", "c", "d,e,f" }.Select(e => e.Split(','))
.   .Aggregate((a,b) => a.Concat(b).ToArray()).ToList()
List<string>(6) { "a", "b", "c", "d", "e", "f" }


ネスト減らしてフラットにするのは 1 段だけです
SelectMany のコールバック関数が 2 次元配列でも 1 次元配列の List になるだけです
> new List<string> { "", "" }.SelectMany(e => new[]{ new[]{ 1, 2 }, new[]{ 3, 4 } }).ToList()
List<int[]>(4) { int[2] { 1, 2 }, int[2] { 3, 4 }, int[2] { 1, 2 }, int[2] { 3, 4 } }



ところでここでいう "リスト"は List 型ではなくて IEnumerable<T> 型を実装するものです