setで重複除去
このレッスンで作るもの — 重複を持たない入れ物
setを使って、実際に使ったカテゴリの一覧を作ります。
set で重複を除く
支出が 30 件あっても、使ったカテゴリは数種類しかありません。「今月はどのカテゴリを使ったか」を出したいとき、素直に書くとこうなります。
categories = []
for record in records:
if record["category"] not in categories:
categories.append(record["category"])動きますが、毎回 not in で全部を調べています。この「同じものを 2 つ持たない」という性質を最初から備えた入れ物が set です。日本語では集合と呼びます。
set の作り方
リストを set() に渡すと、重複が消えた集合になります。
names = ["食費", "日用品", "食費", "食費"]
print(set(names))表示は {'食費', '日用品'} のような形です。食費 が 3 つあっても 1 つにまとまります。波かっこで囲まれますが、辞書とは別ものです。辞書は キー: 値 の組を持ち、集合は値だけを持ちます。
空の集合を作るときだけ注意が必要で、{} は空の辞書になってしまいます。空集合は set() と書きます。
setは「重複を消す」ためだけの道具ではありません。inで「含まれているか」を調べる速さがリストより圧倒的に速い、という利点もあります。件数が増えるほど差が出ます。
順番を持たない
集合の最大の特徴は 順番を持たない ことです。リストは入れた順を守りますが、集合は守りません。
順番が保証されないと、集合をそのまま返す関数は「同じ入力なのに違う結果に見える」ものになってしまいます。表示にも比較にも使えません。
そこで sorted を通してリストに戻します。sorted は集合を受け取ってもきちんと動き、並びの決まったリスト を返してくれます。
categories = sorted(set(["食費", "日用品", "食費"]))
print(categories)['日用品', '食費'] になります。日本語の文字列は文字コードの順に並ぶので、意味の順ではありませんが、毎回必ず同じ並び になります。ここが大事なところです。
「重複を消したいときは
set、結果を人に見せたり比べたりするときはsortedでリストに戻す」。この 2 段構えを型として覚えてしまうと迷いません。
集合に足す
空の集合から作り始めるときは、add で 1 つずつ足します。リストの append にあたるメソッドですが、名前が違うので注意します。
categories = set()
categories.add("食費")
categories.add("食費")
print(len(categories))1 と表示されます。同じ値を 2 回足しても増えません。エラーにもならず、静かに無視されます。この「入れても増えないことがある」という性質が、重複除去そのものです。
動きを追ってみる
食費、日用品、食費、交通費 の 4 件からカテゴリ一覧を作る流れを追います。
- 内包表記やループで
["食費", "日用品", "食費", "交通費"]というリストを作る set(...)に通して{"食費", "日用品", "交通費"}の 3 つになるsorted(...)に通して['交通費', '日用品', '食費']というリストになる- そのリストを
returnする
3 番目で並びが確定します。交通費 が先に来るのは、文字コードの上で 交 が 日 や 食 より小さいためです。
よくある間違い
- 集合をそのまま返す —
return set(...)と書くと、順番が定まらないうえに型もリストではありません。テストの比較も安定しません。必ずsortedでリストにしてから返します。 - 空集合を
{}と書く —{}は空の 辞書 です。空の集合はset()です。type({})を試すとdictと出ます。 - 番号で取り出そうとする — 集合には順番が無いので
categories[0]はTypeErrorになります。取り出したいなら先にリストへ変換します。
集合は
inの判定と重複除去に強く、番号での取り出しと順番の保持ができません。得意なことと苦手なことがはっきりした入れ物です。
やってみよう
関数 used_categories(records) を実装してください。支出の辞書が並んだリストから category を集め、重複を除いた 並び順の決まったリスト を return します。
set で重複を消したあと、必ず sorted でリストにしてから返してください。集合のまま返すと結果が安定しません。空リストを渡されたときは空リスト [] を返します。同じカテゴリが何件あっても、返るのは 1 つだけです。
要件
- 関数名は used_categories で、引数は records の1つ
- 各支出の category を集め、set で重複を取り除く
- sorted でリストに変換してから return する。集合のまま返さない
- 空リストのときは空リストを返す
入出力例
used_categories([{"category":"食費","name":"りんご","price":120},{"category":"日用品","name":"ノート","price":200},{"category":"食費","name":"牛乳","price":180}]) → ["日用品","食費"]
used_categories([{"category":"食費","name":"りんご","price":120},{"category":"食費","name":"牛乳","price":180}]) → ["食費"]
used_categories([]) → []
used_categories([{"category":"食費","name":"りんご","price":120},{"category":"日用品","name":"ノート","price":200},{"category":"娯楽","name":"映画","price":1800},{"category":"交通費","name":"電車代","price":320},{"category":"その他","name":"おみくじ","price":100}]) → ["その他","交通費","娯楽","日用品","食費"]
used_categories([{"category":"娯楽","name":"映画","price":1800},{"category":"交通費","name":"電車代","price":320},{"category":"交通費","name":"バス代","price":210}]) → ["交通費","娯楽"]