つくる:カテゴリ別集計

このレッスンで作るもの — 第 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 totals

if category in totals が要になります。すでにそのカテゴリのキーがあれば今の値に足し、無ければ新しくキーを作って金額を入れます。この分岐を書かずにいきなり totals[category] + record["price"] とすると、初回に KeyError で止まります。

diagram (will load when visible)

totals[category] = totals[category] + record["price"]totals[category] += record["price"] と短く書けます。どちらでも構いません。

動きを追ってみる

りんご 120 食費ノート 200 日用品牛乳 180 食費 の 3 件を集計する流れを追います。

  1. totals{} で始まる
  2. りんご食費 は無いので新しく作り {"食費": 120}
  3. ノート日用品 も無いので作り {"食費": 120, "日用品": 200}
  4. 牛乳食費 はもうあるので 120 + 180{"食費": 300, "日用品": 200}
  5. できた辞書を 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 に流し込むと、カテゴリ別の一覧がそのまま画面に出ます。この章で覚えた道具が一直線につながります。

よくある間違い

  1. 初回の KeyError — 存在確認をせずに足そうとすると、そのカテゴリが初めて出てきた時点で止まります。in で確認するか、無いとき 0 を返す totals.get(category, 0) を使います。
  2. 0 のキーを入れてしまう — 5 カテゴリ分を先に 0 で埋めると、使っていないカテゴリまで結果に残ります。この仕様では入れません。
  3. 合計を文字列で作る — 値は int です。str にすると "120180" のような連結になり、金額として使えなくなります。

やってみよう

関数 total_by_category(records) を実装してください。支出の辞書が並んだリストを受け取り、キーがカテゴリ名、値がそのカテゴリの合計金額 (int) の辞書を return します。

登場しなかったカテゴリはキーに含めません。空リストを渡されたときは空の辞書 {} を返します。同じカテゴリが何件あっても、1 つのキーにまとめて足し込んでください。

要件

  1. 関数名は total_by_category で、引数は records の1つ
  2. キーがカテゴリ名、値がそのカテゴリの合計金額(int)の辞書を return する
  3. 登場しなかったカテゴリはキーに含めない
  4. 空リストを渡されたときは空の辞書 {} を返す

入出力例

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}

ヒント

生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア
編集 LuaGate編集部
main.py
main.py
学習モード

メモ

つくる:カテゴリ別集計

⌘S で保存