リストの処理に関するトリッキーな記法いろいろ

よく忘れるのでメモ。

一定のサイズごとに分ける

>>> a = range(20)
>>> n = 5
>>> zip(*[iter(a)]*n)
[(0, 1, 2, 3, 4), (5, 6, 7, 8, 9), (10, 11, 12, 13, 14), (15, 16, 17, 18, 19)]

個数が割り切れない場合は、切り捨てられるので注意。

>>> a = range(22)
>>> zip(*[iter(a)]*n)
[(0, 1, 2, 3, 4), (5, 6, 7, 8, 9), (10, 11, 12, 13, 14), (15, 16, 17, 18, 19)]

切り捨てないで分けるには、やや煩雑ではあるが次のようにする。

>>> [a[n*i:n*(i+1)] for i in xrange(len(a)/n+1)]
[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20, 21]]
>>> map(lambda *x: filter(lambda x: x is not None, x), *[iter(a)]*n)
[(0, 1, 2, 3, 4), (5, 6, 7, 8, 9), (10, 11, 12, 13, 14), (15, 16, 17, 18, 19), (20, 21)]

2次元配列の転置

>>> a = [range(4) for i in range(4)]
>>> a
[[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]]
>>> map(list, zip(*a))
[[0, 0, 0, 0], [1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]]

個数が合わない場合は、やはり切り捨てられるので注意。

>>> a = [range(2), range(3), range(4)]
>>> a
[[0, 1], [0, 1, 2], [0, 1, 2, 3]]
>>> map(list, zip(*a))
[[0, 0, 0], [1, 1, 1]]

2次元配列の1次元化

sum()は第2引数に初期値を与えることができ、明示的に空リストを指定することでサブリストの結合ができる。

>>> a = [range(4) for i in range(4)]
>>> a
[[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]]
>>> sum(a, [])
[0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]

あるいは、itertools.chain()を使う。

>>> import itertools
>>> list(itertools.chain(*a))
[0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]

最も長い文字列を選ぶ

list.sort()sorted()と同様に、max()もキーワード引数として比較キーを計算する関数を与えることができ、len()を指定することで文字列長などが最も長い要素を1個選ぶことができる。

>>> a = "hello nice to meet you".split()
>>> a
['hello', 'nice', 'to', 'meet', 'you']
>>> max(a, key=len)
'hello'

min()についても同じである。

>>> min(a, key=len)
'to'

二つの配列の直積

あまりトリッキーではないけどついでに。

>>> a = range(2)
>>> b = range(3)
>>> [(i,j) for i in a for j in b]
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]

あるいは、itertools.product()を使う。

>>> import itertools
>>> list(itertools.product(a,b))
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]