シーケンスの順番を維持したままuniq化する
リストの順番を維持したままユニーク化(=後に出てくる同じ要素はカットする)を行う方法です。
>>> # (Python 2.6) >>> L = [5,2,6,5,2,3,6,1,4,2] >>> list(set(L)) [1, 2, 3, 4, 5, 6] >>> [x for x in L if x not in locals()['_[1]'] ] [5, 2, 6, 3, 1, 4]
setを使った方法は順番が変わっていますが、2番目の方法は順番が維持されています。
これは「リスト内包表記の作成途中のリストを読み出す」裏技を使っています。「_[1]」という変数に作成途中のリストが入っているのですが、これは「変数_のインデックス1」という意味ではなく「_[1]」というひとつながりの変数名です。locals()を使わないとアクセスできません。
リスト内包表記をネストさせた場合は「_[2]」「_[3]」とかも作られるようです。
この技はいろいろなところで紹介されていて、さらに応用して「リスト内包表記だけで素数の列を作る」なんて大技も披露されていました。
しかしこの裏技はPython 2.7でなくなりました。locals()['_[1]'] はKeyErrorとなります。
ということで、uniq化の別の方法として「どう書く?org」に載っている方法を挙げます。
http://ja.doukaku.org/16/lang/python/
>>> sorted(set(L), key=L.index) [5, 2, 6, 3, 1, 4]
これが一番綺麗な方法だと思います。要素数が極端に多いとindexやsortedにかかる時間が気になるかもしれないですが。
>>> reduce(lambda a,b: a+[b] if b not in a else a, L, []) [5, 2, 6, 3, 1, 4]
reduceを使う方法。何となくこれが一番速そうな気もしますが... 後で測定してみようかな。
def uniq(it): s = set() for x in it: if x not in s: yield x s.add(x) print list(uniq(L))
forループで速度が気になりますが、要素が少ないなら実直に処理するのもありかもしれません。