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

akiyoko blog

akiyoko の IT技術系ブログです

Python でリストのソートまとめ

f:id:akiyoko:20141025203133j:plain


今回は、Python でリストをソートするやり方をまとめてみます。

環境

Python 2.7 で動作確認




 

list.sort() でソート

破壊的なメソッドなので、元のリストオブジェクトの順序を入れ替えてしまいます。
あまり使うことは少ないのではないでしょうか。


次に紹介する「sorted」と比較して、

違いは他にもあります、 list.sort() メソッドはリストにのみ定義されています。一方 sorted() 関数は任意のイテラブルを受け付けます。


ソート HOW TO — Python 2.7ja1 documentation」より

という違いがあります。


< Sample >

>>> a = [3, 2, 1, 5, 4]
>>> a.sort()
>>> a
[1, 2, 3, 4, 5]

>>> b = ['c', 'a', 'b', 'Z', '+']
>>> b.sort()
>>> b
['+', 'Z', 'a', 'b', 'c']

文字列は、コードポイント順にソートされます。


ちなみにコードポイントとは、文字一つ一つに割り当てられた番号みたいなものです。

>>> ord('c')
99
>>> ord('a')
97
>>> ord('b')
98
>>> ord('Z')
90
>>> ord('+')
43

記号が先頭に、そして大文字の方が小文字よりも前に来るのは、このためですね。

(参考)




 

sorted でソート

組み込み関数 sorted を使う方法です。
実際には、list.sort() よりもこちらの方がよく使うのではないでしょうか。
sorted() はリスト(正しくはイテラブルなオブジェクト)を引数に取り、ソートされたリストを戻り値として返し、元のリストオブジェクトは壊しません。


< Sample >

>>> a = [3, 2, 1, 5, 4]
>>> sorted(a)
[1, 2, 3, 4, 5]

>>> b = ['c', 'a', 'b', 'Z', '+']
>>> sorted(b)
['+', 'Z', 'a', 'b', 'c']

>>> persons = [('Yoshida', 30), ('Suzuki', 100), ('Tanaka', 15)]
>>> sorted(persons)
[('Suzuki', 100), ('Tanaka', 15), ('Yoshida', 30)]

最後の例では、タプルのリストをソートしていますが、各タプルの0番目の要素で並べ替えがされています。





 

keyパラメータを使った柔軟なソート

Python 2.4 から、 list.sort() と sorted() には key パラメータが追加されました、これは比較を行う前にリストの各要素に対して呼び出される関数を指定するパラメータです。


ソート HOW TO — Python 2.7ja1 documentation」より


例えば、単に

>>> a = ['123', '1', '9']
>>> sorted(a)
['1', '123', '9']

とすると、「123」が「9」より先に来てしまって困るなぁ、というケースがあります。


keyパラメータに int関数を渡すと、

>>> a = ['123', '1', '9']
>>> sorted(a, key=int)
['1', '9', '123']

という感じで、数値でソートすることができます。



keyパラメータには lambda式も渡せるので、もっと柔軟なソートも可能です。

>>> persons = [('Yoshida', 30), ('Suzuki', 100), ('Tanaka', 15)]
>>> sorted(persons, key=lambda x: x[1])
[('Tanaka', 15), ('Yoshida', 30), ('Suzuki', 100)]

>>> persons = [('Yoshida', '30'), ('Suzuki', '100'), ('Tanaka', '15')]
>>> sorted(persons, key=lambda x: int(x[1]))
[('Tanaka', '15'), ('Yoshida', '30'), ('Suzuki', '100')]



keyパラメータは、クラスを使ったオブジェクトのリストなどを扱う場合に、非常に有効だったりします。

>>> class Person(object):
...     def __init__(self, name, age):
...         self.name = name
...         self.age = age
...     def __repr__(self):
...         return '{name: %s, age: %s}' % (self.name, self.age)
... 
>>> persons = [Person('Yoshida', 30), Person('Suzuki', 100), Person('Tanaka', 15)]
>>> sorted(persons, key=lambda x: x.age)
[{name: Tanaka, age: 15}, {name: Yoshida, age: 30}, {name: Suzuki, age: 100}]




 

重複を除去する set との併用

リストから重複を除去するのに、set を使うことがあると思うのですが、

>>> a = ['c', 'a', 'a', 'b', 'c', 'a', 'c']
>>> list(set(a))
['a', 'c', 'b']

set では順序は保証されません。


sorted を使って、

>>> a = ['c', 'a', 'a', 'b', 'c', 'a', 'c']
>>> sorted(set(a))
['a', 'b', 'c']

このようにコードポイント順に並べることはできますが、元の要素の順番はそのままにしたいという場合はどうしたらよいでしょうか?


結論から言うと、
setで重複を除去しつつ、元の要素の順番そのままにしたい場合は、keyパラメータに indexメソッドを指定します。

>>> a = ['c', 'a', 'a', 'b', 'c', 'a', 'c']
>>> sorted(set(a), key=a.index)
['c', 'a', 'b']

元のリストオブジェクトのindexを参照すればよいということですね。


ちなみに indexは、リストの中の要素の位置を返すメソッドです。

>>> a = ['c', 'a', 'a', 'b', 'c', 'a', 'c']
>>> a.index('c')
0
>>> a.index('a')
1
>>> a.index('b')
3

>>> b = 'caabcac'
>>> b.index('c')
0
>>> b.index('a')
1
>>> b.index('b')
3

という感じで使います(が、実際に使うことはあまりないはず)。


 

参考



こちらは、「Python 3 エンジニア認定基礎試験」の認定教材にもなっている一冊です。この一冊で Python の基礎構文をしっかりと身に付けることができます。


<参考記事>
akiyoko.hatenablog.jp



Python 初心者であれば、こちらをオススメします。