note.nkmk.me

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

Date: 2018-05-03 / Modified: 2018-05-19 / tags: 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に指定した関数の結果に従ってソートされる。

今回の例ではリストの要素である辞書から特定のキーの値を取得する関数を指定すればよい。

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()

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事