読者です 読者をやめる 読者になる 読者になる

Pythonでリストの内包表記を使って2次元リストを1次元リストにする

やりたかったことは

list = [[1, 3, 5], [2,4]]

こういう2次元のリストを

[1, 3, 5, 2, 4]

こういう1次元リストにしたくてどうすればいいのかなぁと。
for文作ればすぐできそうだったのですが、大した処理でもないので1行で書けないのかなと思いました。



「多分、こういうときはリストの内包表記ってやつを使うんだろうな」と予測を立てて、まずはググった。
まずは「python 2次元 1次元 リスト 内包表記」ってキーワードでググったけども上手くヒットせず。
「内包表記とかがダメなのかなぁ?」と思ってキーワードから抜いても上手くヒットせず
色々試したけどどうもヒットしない。



あんまり需要ないのかもしれないなと思い、自分で組み立て。

>>> list = [[1, 3, 5], [2, 4]]
>>> [item for item in v for v in list]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'v' is not defined

何かエラー出た。なんだこれ。



頭の中で「listをforで回して[1, 3, 5]と[2, 4]を取り出して、その後さらにそれぞれその中を表示すればいいんだよな」と思って作った式が上のやつ。
for文使ってそのまま書くと

>>> items = []
>>> for v in list:
...   for item in v:
...     items.append(item)
... 
>>> items
[1, 3, 5, 2, 4]

いけた。あれ…いけるじゃないか。



何が間違ってたかわからなくてチュートリアルを確認。

>>> vec1 = [2, 4, 6]
>>> vec2 = [4, 3, -9]
>>> [x*y for x in vec1 for y in vec2]
[8, 6, -18, 16, 12, -36, 24, 18, -54]
>>> # 訳注: 上記の内包表記をループで書きなおすと、こうなります。
>>> L = []
>>> for x in vec1:
...     for y in vec2:
...         L.append(x*y)

あれ?あれ?前のforから読むの?Perlのmapみたいに後ろから見てけばいいものかと思ったのに。
もう少し読み進めると、

ノート
訳注: 内包表記の中に for や if が複数回現れる場合、 左側の for, if が、内包表記ではなくループで書いた場合の 外側のブロックになります。 上の例はまだ判りやすいですが、複雑な内包表記はすぐに読みにくくなってしまうので、 その場合はループで書き下した方が良いでしょう。

なるほど。そうなのね。だからvが定義されてないってエラー出たのか。for文が複数ある場合は前から見ていいのね。



ということで初めに書いた式のfor文を入れ替えて以下のようにしたら上手くいきました。

>>> list = [[1, 3, 5], [2, 4]]
>>> [flatten for inner in list for flatten in inner]
[1, 3, 5, 2, 4]

余談ですが、これ書きながら「そういえば1次元配列にするときにflattenって変数名や関数名使うのみたことあるな」と思い、キーワードを「two-dimensional python list flatten」にしてみたら結構出てきました。
例えば下のやつ。

ふーむ。全く同じことやってる。ググる能力が足りなかった。