リストの要素が単純な値ではない場合、それを自分が思った通りの順番に並べ替えるにはちょっとした知識が必要になります。覚えているかどうかを確認してみましょう。
以下のnames_and_ranksは「名前と何らかのランクの組みからなるタプルを要素とするリスト」である。このリストのsortメソッドを呼び出すと、その名前の順番に並べ替えられる。そうではなく、ランクの昇順に並べ替えたいとしたら、このコードをどのように修正すればよいだろう。
names_and_ranks = [('mike', 5), ('pochi', 2),
('tama', 4), ('kuro', 1),
('shiro', 3)]
names_and_ranks.sort()
print(names_and_ranks) # そのままだと名前順に並べ替えられる
# 出力結果:
# [('kuro', 1), ('mike', 5), ('pochi', 2),
# ('shiro', 3), ('tama', 4)]
# 上のコードを修正して、次のようにランク順に並べ替えるにはどうする?
# [('kuro', 1), ('pochi', 2), ('shiro', 3),
# ('tama', 4), ('mike', 5)]
正解のコード例を以下に示します。
names_and_ranks = [('mike', 5), ('pochi', 2),
('tama', 4), ('kuro', 1),
('shiro', 3)]
# タプルの第1要素(ランク)を選択するラムダ式をkeyパラメーターに指定
names_and_ranks.sort(key=lambda x: x[1])
print(names_and_ranks)
タプルの第1要素(ランク)を選択するラムダ式を、リストのsortメソッドのkeyパラメーターに与えることで、ランクをキーとして並べ替えを行えます。
リストには要素をインプレースで並べ替えるためのsortメソッドがあります。そして、sortメソッドにはkeyパラメーターがあり、このパラメーターに「引数を1個取る関数」を与えることで、並べ替えに使うキーを指定したり、計算したりできます。keyパラメーターを指定しないときには、要素同士の大小関係を小なり演算子「<」で比較した結果によって並べ替えが行われます。
リストのsortメソッドは、呼び出しに使ったリストそのものを変更してしまいます。そうではなく、新しいリストが必要ならPythonに組み込みのsorted関数を使うとよいでしょう。この関数は引数に与えたリスト(などの反復可能オブジェクト)を並べ替えた新しいリストを返すもので、sortメソッドと同じ役割を持つkeyパラメーターがあります。
keyパラメーターには関数やメソッド、ラムダ式など呼び出し可能で引数を1個取るものを指定します。sortメソッドでは、リストの要素を引数として、keyパラメーターに指定された関数を呼び出し、その関数が返す戻り値を基に並べ替えが実行されます。
以下のように大文字小文字を含んだ英単語を要素とするリストを、大文字小文字の関係なしにアルファベット順に並べ替えるのはよくある例です。このときには文字列のupperメソッドまたはlowerメソッドをkeyパラメーターに与え、全ての要素を大文字あるいは小文字にそろえることで、大文字小文字の違いを吸収してしまいます。
str_list = ['Foo', 'bAR', 'Baz']
str_list.sort(key=str.lower) # 全てを小文字化したものを並べ替えのキーとする
print(str_list) # ['bAR', 'Baz', 'Foo']
問題は「タプルの第1要素(ランク)を基にリストの要素を並べ替えたい」というものでした。そして、タプルから特定の要素を取り出すという関数はPythonには用意されていません。そのため、正解のコード例では、タプルを与えるとその第1要素を返すようなラムダ式をkeyパラメーターに指定しています。これにより、ランクの値をキーとして並べ替えが行われるようになるわけです。
単なる要素の並べ替えではなく、何らかのロジックを基にリストの要素を並べ替えたいときにはkeyパラメーターをうまく使えることを覚えておきましょう。
これで終わってしまってもよいのですが、例によって「正解は1つじゃない」ことをご紹介しておきましょう。ヘンテコなコードなので、無理にこんなことをする必要はありません。
コードを紹介する前に一つ。keyパラメーターを指定しない場合は、要素同士を小なり演算子「<」で比較した結果を基に並べ替えが行われます。問題ではリストの要素はタプルなので、この場合はタプル同士の比較が行われるということです。タプル同士の比較では、その第0要素同士が比較され、それが同じなら第1要素同士を比較して、それも同じなら……といった具合に大小関係が決まります。
print((0, 1) == (0, 1)) # True
print((0, 1, 2) == (0, 1, 3)) # False
print((0, 1, 2) < (0, 1, 3)) # True
ということは、「(名前, ランク)」というタプルを「(ランク, 名前)」というタプルにすればkeyパラメーターを指定しなくてもランク順に並べ替えられそうです。実際にやってみましょう(ここではsortメソッドではなく、sorted関数を使ってみました。その理由は以下のコードにある「sorted_by_rank = ……」と「result = ……」の2行を1行の内包表記に書き直してみると分かるはずです。興味のある方は書き直してみてください)。
names_and_ranks = [('mike', 5), ('pochi', 2),
('tama', 4), ('kuro', 1),
('shiro', 3)]
sorted_by_rank = sorted((r, n) for n, r in names_and_ranks)
result = [(n, r) for r, n in sorted_by_rank]
print(result) # 同じ結果
まあ、これと同じことはkeyパラメーターに「lambda x: (x[1], x[0])」と書けばできちゃうんですよね。さらにいえば、そう書くくらいなら「lambda x: x[1]」と書くだけでいいじゃんって話になっちゃうのですが……。
Copyright© Digital Advantage Corp. All Rights Reserved.