Pythonで辞書のリストを特定のキーの値に従ってソート

Pythonで共通のキーを持つ辞書を要素とするリストをsort()メソッドやsorted()関数でソートしようとすると、デフォルトではエラーTypeErrorになってしまいます。 sort()メソッドやsorted()関数の引数keyを指定することで辞書のリストを特定のキーの値に従ってソートできます。 共通のキーを持つ辞書を要素とするリストを例にします。出力を見やすくするためpprintモジュールを使っています。

import pprint

l = [{'Name': 'Alice', 'Age': 40, 'Point': 80},
     {'Name': 'Bob', 'Age': 20},
     {'Name': 'Charlie', 'Age': 30, 'Point': 70}]

このような辞書のリストはJSONを読み込むと頻繁に遭遇します。PythonでのJSONの読み書きは

ここでは以下の内容について説明します。

デフォルトでは辞書のリストをソートするとエラー 引数keyに無名関数(ラムダ式)を指定 引数reverseで降順・昇順を指定 sort()メソッドでもsorted()関数でも同様

同様の方法で2次元配列(リストのリスト)をソートすることもできます。

なお、このような辞書のリストはデータ分析ライブラリpandasのpandas.DataFrameに変換することができます。もろもろの処理をするのであればpandas.DataFrameに変換したほうが何かと便利です。

デフォルトでは辞書のリストをソートするとエラー

辞書(dict型オブジェクト)のリストをsort()メソッドやsorted()関数でソートしようとすると、デフォルトではエラーTypeErrorになってしまいます。 これは辞書オブジェクトの大小比較(<や>などでの演算)がサポートされていないため。

# l.sort()
# TypeError: '<' not supported between instances of 'dict' and 'dict'

引数keyに無名関数(ラムダ式)を指定

辞書のリストを特定のキーの値に従ってソートしたい場合は、sort()メソッドやsorted()関数の引数keyを指定します。 keyには、ソートされる(各要素が比較される)前にリストの各要素に適用される関数を指定します。keyに指定した関数の結果に従ってソートされます。 今回の例ではリストの要素である辞書から特定のキーの値を取得する関数を指定すればOKです。 defで関数を定義してもいいが、このような場合は無名関数(ラムダ式)を使うと便利です。

l.sort(key=lambda x: x['Age'])

pprint.pprint(l)
# [{'Age': 20, 'Name': 'Bob'},
#  {'Age': 30, 'Name': 'Charlie', 'Point': 70},
#  {'Age': 40, 'Name': 'Alice', 'Point': 80}]

l.sort(key=lambda x: x['Name'])

pprint.pprint(l)
# [{'Age': 40, 'Name': 'Alice', 'Point': 80},
#  {'Age': 20, 'Name': 'Bob'},
#  {'Age': 30, 'Name': 'Charlie', 'Point': 70}]

無名関数(ラムダ式)について

共通のキーを持たない要素が存在する場合 上で示した方法だと、共通のキーを持たない要素が存在する場合にエラーとなります。

# l.sort(key=lambda x: x['Point'])
# KeyError: 'Point'

そのような場合は、存在しないキーに対しても値を取得できる辞書のget()メソッドを使います。

デフォルトでは存在しないキーに対してNoneを返すので、数値や文字列とは比較できずにエラーとなります。

# l.sort(key=lambda x: x.get('Point'))
# TypeError: '<' not supported between instances of 'int' and 'NoneType'

第二引数に存在しないキーに対して返す値を指定できます。キーが存在しない要素は第二引数に指定した値に置き換えられてソートされます。

l.sort(key=lambda x: x.get('Point', 0))

pprint.pprint(l)
# [{'Age': 20, 'Name': 'Bob'},
#  {'Age': 30, 'Name': 'Charlie', 'Point': 70},
#  {'Age': 40, 'Name': 'Alice', 'Point': 80}]

l.sort(key=lambda x: x.get('Point', 100))

pprint.pprint(l)
# [{'Age': 30, 'Name': 'Charlie', 'Point': 70},
#  {'Age': 40, 'Name': 'Alice', 'Point': 80},
#  {'Age': 20, 'Name': 'Bob'}]

引数reverseで降順・昇順を指定

降順・昇順は引数reverseで指定します。

l.sort(key=lambda x: x['Name'], reverse=True)

pprint.pprint(l)
# [{'Age': 30, 'Name': 'Charlie', 'Point': 70},
#  {'Age': 20, 'Name': 'Bob'},
#  {'Age': 40, 'Name': 'Alice', 'Point': 80}]

sort()メソッドでもsorted()関数でも同様

ここまでの例はリストのメソッドsort()を使っているが、組み込み関数sorted()でも同様に引数keyや引数reverseを指定できます。

l_sorted = sorted(l, key=lambda x: x['Age'], reverse=True)

pprint.pprint(l_sorted)
# [{'Age': 40, 'Name': 'Alice', 'Point': 80},
#  {'Age': 30, 'Name': 'Charlie', 'Point': 70},
#  {'Age': 20, 'Name': 'Bob'}]

sort()とsorted()の違いについては元のオブジェクト自体を並び替えるのがsort()で、並び替えた新たなオブジェクトを生成するのがsorted()。

Last Updated: 6/26/2019, 10:34:03 PM