つくる:カテゴリ別集計
このレッスンで作るもの — 第 7 章の総仕上げとして、支出をカテゴリごとに合計する
total_by_categoryを作ります。
つくる — カテゴリ別集計
第 6 章の summarize は、全体の合計と予算の判定を返すところまででした。使ってみると、次にほしくなるのは決まって「何にいくら使ったのか」です。食費が多いのか娯楽が多いのかが分かって、はじめて家計簿は役に立ちます。
この章で覚えた辞書がそのまま答えになります。作るのは下記の形を返す関数です。
{"食費": 300, "日用品": 200}キーがカテゴリ名、値がそのカテゴリの合計金額です。カテゴリの数は最初から決まっていないので、リストではなく辞書で持つのが自然です。
「名前ごとに数字をためる」場面は辞書のいちばん典型的な使い道です。この形は集計処理の基本形として、この先どの言語でも出てきます。
出てこなかったカテゴリは入れない
仕様で 1 つだけ決めておくことがあります。使わなかったカテゴリを 0 で入れるか、キーごと入れないかです。
このアプリでは 入れない ことにします。食費しか使っていない月に {"食費": 300, "娯楽": 0, "交通費": 0, "日用品": 0, "その他": 0} が返ってきても、読むときに邪魔なだけだからです。支出が 1 件も無ければ、空の辞書 {} を返します。
仕様の細かい決めごとは「どちらが正しいか」ではなく「どちらが使いやすいか」で選びます。決めたら本文とテストで揺らさないことのほうが大事です。
たまっていく辞書を作る
書き方の骨組みは下記のとおりです。空の辞書から始めて、1 件ずつ足していきます。
def total_by_category(records):
totals = {}
for record in records:
category = record["category"]
if category in totals:
totals[category] = totals[category] + record["price"]
else:
totals[category] = record["price"]
return totalsif category in totals が要になります。すでにそのカテゴリのキーがあれば今の値に足し、無ければ新しくキーを作って金額を入れます。この分岐を書かずにいきなり totals[category] + record["price"] とすると、初回に KeyError で止まります。
totals[category] = totals[category] + record["price"] は totals[category] += record["price"] と短く書けます。どちらでも構いません。
動きを追ってみる
りんご 120 食費、ノート 200 日用品、牛乳 180 食費 の 3 件を集計する流れを追います。
totalsは{}で始まるりんご—食費は無いので新しく作り{"食費": 120}ノート—日用品も無いので作り{"食費": 120, "日用品": 200}牛乳—食費はもうあるので120 + 180で{"食費": 300, "日用品": 200}- できた辞書を
returnする
キーの並びは「最初に出てきた順」になります。辞書は入れた順を保つので、食費 が先、日用品 があとです。
辞書どうしを比べるとき、Python はキーの並びを見ません。
{"a": 1, "b": 2}と{"b": 2, "a": 1}は等しいと判定されます。並びを気にせず書けます。
手元で動かしてみる
kakeibo.py に足して、第 6 章までの関数と並べて呼んでみます。
records = [
{"name": "りんご", "price": 120, "category": "食費"},
{"name": "ノート", "price": 200, "category": "日用品"},
{"name": "牛乳", "price": 180, "category": "食費"},
]
for category, total in total_by_category(records).items():
print(f"{category} {total}円")items() で回して f-string に流し込むと、カテゴリ別の一覧がそのまま画面に出ます。この章で覚えた道具が一直線につながります。
よくある間違い
- 初回の
KeyError— 存在確認をせずに足そうとすると、そのカテゴリが初めて出てきた時点で止まります。inで確認するか、無いとき0を返すtotals.get(category, 0)を使います。 0のキーを入れてしまう — 5 カテゴリ分を先に0で埋めると、使っていないカテゴリまで結果に残ります。この仕様では入れません。- 合計を文字列で作る — 値は
intです。strにすると"120180"のような連結になり、金額として使えなくなります。
やってみよう
関数 total_by_category(records) を実装してください。支出の辞書が並んだリストを受け取り、キーがカテゴリ名、値がそのカテゴリの合計金額 (int) の辞書を return します。
登場しなかったカテゴリはキーに含めません。空リストを渡されたときは空の辞書 {} を返します。同じカテゴリが何件あっても、1 つのキーにまとめて足し込んでください。
要件
- 関数名は total_by_category で、引数は records の1つ
- キーがカテゴリ名、値がそのカテゴリの合計金額(int)の辞書を return する
- 登場しなかったカテゴリはキーに含めない
- 空リストを渡されたときは空の辞書 {} を返す
入出力例
total_by_category([{"category":"食費","name":"りんご","price":120},{"category":"日用品","name":"ノート","price":200},{"category":"食費","name":"牛乳","price":180}]) → {"日用品":200,"食費":300}
total_by_category([]) → {}
total_by_category([{"category":"娯楽","name":"映画","price":1800}]) → {"娯楽":1800}
total_by_category([{"category":"交通費","name":"電車代","price":320},{"category":"交通費","name":"バス代","price":220}]) → {"交通費":540}
total_by_category([{"category":"食費","name":"りんご","price":120},{"category":"日用品","name":"ペン","price":150},{"category":"娯楽","name":"映画","price":1800},{"category":"交通費","name":"電車代","price":320},{"category":"その他","name":"おみくじ","price":100},{"category":"食費","name":"牛乳","price":180}]) → {"その他":100,"交通費":320,"娯楽":1800,"日用品":150,"食費":300}
total_by_category([{"category":"その他","name":"試供品","price":0}]) → {"その他":0}