LoginSignup
0
2

More than 5 years have passed since last update.

Linqで1行ずつ与えられた、データを縦に分割しよう!(ToLookup使用)

Posted at

目的

1行ずつ与えられるデータ(ファイルとか、標準入力とか)が重なって2次元配列みたいなデータを列で分割して、楽に変換したい。

サンプル

例えば、こんなデータがあったとします。

Sample.cs
var str = new List<string>()
              {
                  "  1,  2,  3,  4,  5,  6,  7,  8,  9, 10",
                  "  2,  4,  6,  8, 10, 12, 14, 16, 18, 20",
                  "  3,  6,  9, 12, 15, 18, 21, 24, 27, 30",
                  "  4,  8, 12, 16, 20, 24, 28, 32, 36, 40",
                  "  5, 10, 15, 20, 25, 30, 35, 40, 45, 50",
                  "  6, 12, 18, 24, 30, 36, 42, 48, 54, 60",
                  "  7, 14, 21, 28, 35, 42, 49, 56, 63, 70",
                  "  8, 16, 24, 32, 40, 48, 56, 64, 72, 80",
                  "  9, 18, 27, 36, 45, 54, 63, 72, 81, 90",
                  " 10, 20, 30, 40, 50, 60, 70, 80, 90,100",
              };

これを列(1列目だと、1,2,3, ... , 10 のように)ごとに取得したい。

for文で

forなら簡単に取得ができる

ForSample.cs
            var sample = new List<string>();
            for (int i = 0; i < str.Count; i++)
            {
                sample.Add(str[i].Split(',')[0]);
            }

            Console.WriteLine("forサンプル");
            Console.WriteLine(string.Join(",", sample));

こんな感じでこれを実行すると、こんな感じになる
image.png

1列目だけでいいのなら、このサンプルコードでもいいのだけど、複数行対応したいときにはネストをしないといけなくなる。
例えばこんな感じ

AllFor.cs
//// 1つ目のデータで配列の数を設定
            var length = str[0].Split(',').Length;

            // 各列のリストを初期化
            var sample = new List<List<string>>();
            for (int i = 0; i < length; i++)
            {
                sample.Add(new List<string>());
            }

            // 1行をカンマで分割し、それぞれのリストに追加していく
            for (int i = 0; i < str.Count; i++)
            {
                var oneLine = str[i].Split(',');
                for (int j = 0; j < length; j++)
                {
                    sample[j].Add(oneLine[j]);
                }
            }

            // 表示
            for (int i = 0; i < length; i++)
            {
                Console.WriteLine($"i : {i}");
                Console.WriteLine(string.Join(",", sample[i]));
            }

冗長な部分もあると思いますが、素直に書いたら大体こんな感じになると思います。意外と長くなってしまいます。

Linqで

次はLinqで書いてみたいと思います。こちらの方がやはり少ない行数で書けるということで、いいですね。慣れるまでは大変ですが。

LinqSample.cs
            // 1行をカンマで分割して、0からインデックスを追加
            var strWithIndex = str.Select(
                x => x.Split(',').Where(word => !string.IsNullOrWhiteSpace(word)).Select((word, i) => new { word, i }));
            // 全ての行を平坦化して、先ほど追加したインデックスでグループ化する.
            var oneColumnData = strWithIndex.SelectMany(word => word).ToLookup(x => x.i);

一応この2行で、列で取得することが可能です。
実際に取得するとこんな感じです。

LinqOutput.cs
            foreach (var oneColumn in oneColumnData)
            {
                Console.WriteLine("キー:" + oneColumn.Key);
                Console.WriteLine(string.Join(",", oneColumn.Select(x => x.word)));
            }

image.png

これで、2次元配列とかも簡単に列で取得することができますね!

簡単な解説

このままで終わるのもあれなので、一応解説をしておきます。

まず、LinqSample.csの1行目はコメントにも書いてある通り、1行をまずカンマで分割し、そのあと分割後の要素が{空、Null、string.Empty}ではない要素のみ取得し、その要素にインデックスを付与しています。
1つだけ実行するとこんな感じ
image.png
ここでの、iの値がそのまま列のインデックスとなります。

2つ目は、全部を列挙できるような状態に直してからインデックスでグループを作っていきます。

全部列挙(SelectMany(x => x))後の状態
image.png

ToLookupでグループ化した状態は先ほど見せた画像と同じです。
image.png

終わりに

いかがだったでしょうか?
次があれば、自分が便利だと思った、C#の標準メソッドなどを紹介したいです。
それではよいC#ライフを!

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2