keyでソート
辞書のリストは、そのままでは並べ替えられない
数値のリストなら、sorted に渡すだけで並びます。
Python
prices = [320, 120, 1800, 200]
print(sorted(prices))
print(sorted(prices, reverse=True))[120, 200, 320, 1800] と [1800, 320, 200, 120] が出ます。既定は小さい順で、reverse=True を足すと大きい順になります。sorted は 新しいリスト を返すので、元の prices は並び替わりません。
ところが支出の記録は、辞書が並んだリストです。これをそのまま sorted に渡すと TypeError になります。辞書と辞書のどちらが大きいか、Python は決められないからです。
何を物差しにするかを、こちらから教える
sorted には key という引数があります。ここに関数を渡すと、sorted は要素を 1 つずつその関数に通し、返ってきた値どうし を比べます。
Python
records = [
{"name": "りんご", "price": 120, "category": "食費"},
{"name": "映画", "price": 1800, "category": "娯楽"},
]
result = sorted(records, key=lambda r: r["name"])lambda r: r["name"] は「r を受け取って品名を返す小さな関数」です。品名を物差しにしたので、辞書は品名の順に並びます。def で名前を付けるほどでもない処理をその場に書ける、というのが lambda の使いどころでした。物差しを別のキーに変えれば、その基準で並びます。
大事なのは、並ぶのは物差しの値ではなく元の辞書のまま ということです。key は比べるためだけに使われ、結果のリストに入るのは元の要素です。ここを取り違えると「品名のリストが返ると思ったのに辞書が返る」と戸惑います。
渡すのは関数そのもので、呼ぶのは sorted の仕事です。key=lambda r: r["name"]() のようにかっこを足すと関数ではなくなります。key と reverse はどちらも名前を書いて渡すキーワード引数なので、sorted(records, lambda r: r["name"]) のように名前を省くとエラーになります。
元の並びを壊さずに済む
sorted と似た名前で records.sort() というメソッドもあります。こちらは元のリストを並べ替えてしまい、戻り値は None です。records = records.sort() と書いて中身を丸ごと失う事故が起きやすいので、このコースでは sorted に統一します。
同じ物差しの値が並んだときは、順番が入れ替わりません。元の並びが保たれるので、何度実行しても同じ結果になります。降順にしたいときに sorted(...)[::-1] で見た目だけ逆にすると、この性質が崩れます。逆順が必要なら reverse を使います。
やってみよう
関数 sort_by_price_desc(records) を実装してください。支出の辞書が並んだリストを、price の大きい順に並べ替えた 新しいリスト を return します。
key に金額を返す lambda を渡し、大きい順にする指定を足せば 1 行で書けます。元のリストは書き換えず、sort メソッドではなく sorted を使ってください。空リストのときは空リストがそのまま返ります。
要件
- 関数名は sort_by_price_desc で、引数は records の1つ
- sorted の key に lambda を渡し、price を基準にする
- reverse=True を指定して金額の大きい順にする
- 元のリストを書き換えず、新しいリストを return する
入出力例
sort_by_price_desc([{"category":"食費","name":"りんご","price":120},{"category":"娯楽","name":"映画","price":1800},{"category":"交通費","name":"電車代","price":320}]) → [{"category":"娯楽","name":"映画","price":1800},{"category":"交通費","name":"電車代","price":320},{"category":"食費","name":"りんご","price":120}]
sort_by_price_desc([{"category":"娯楽","name":"映画","price":1800},{"category":"日用品","name":"ノート","price":200}]) → [{"category":"娯楽","name":"映画","price":1800},{"category":"日用品","name":"ノート","price":200}]
sort_by_price_desc([{"category":"その他","name":"試供品","price":0},{"category":"食費","name":"牛乳","price":180},{"category":"日用品","name":"ペン","price":150}]) → [{"category":"食費","name":"牛乳","price":180},{"category":"日用品","name":"ペン","price":150},{"category":"その他","name":"試供品","price":0}]
sort_by_price_desc([{"category":"食費","name":"りんご","price":120}]) → [{"category":"食費","name":"りんご","price":120}]
sort_by_price_desc([]) → []