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 で「含まれているか」を調べる速さがリストより圧倒的に速い、という利点もあります。件数が増えるほど差が出ます。

順番を持たない

集合の最大の特徴は 順番を持たない ことです。リストは入れた順を守りますが、集合は守りません。

diagram (will load when visible)

順番が保証されないと、集合をそのまま返す関数は「同じ入力なのに違う結果に見える」ものになってしまいます。表示にも比較にも使えません。

そこで 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 件からカテゴリ一覧を作る流れを追います。

  1. 内包表記やループで ["食費", "日用品", "食費", "交通費"] というリストを作る
  2. set(...) に通して {"食費", "日用品", "交通費"} の 3 つになる
  3. sorted(...) に通して ['交通費', '日用品', '食費'] というリストになる
  4. そのリストを return する

3 番目で並びが確定します。交通費 が先に来るのは、文字コードの上で より小さいためです。

よくある間違い

  1. 集合をそのまま返すreturn set(...) と書くと、順番が定まらないうえに型もリストではありません。テストの比較も安定しません。必ず sorted でリストにしてから返します。
  2. 空集合を {} と書く{} は空の 辞書 です。空の集合は set() です。type({}) を試すと dict と出ます。
  3. 番号で取り出そうとする — 集合には順番が無いので categories[0]TypeError になります。取り出したいなら先にリストへ変換します。

集合は in の判定と重複除去に強く、番号での取り出しと順番の保持ができません。得意なことと苦手なことがはっきりした入れ物です。

やってみよう

関数 used_categories(records) を実装してください。支出の辞書が並んだリストから category を集め、重複を除いた 並び順の決まったリストreturn します。

set で重複を消したあと、必ず sorted でリストにしてから返してください。集合のまま返すと結果が安定しません。空リストを渡されたときは空リスト [] を返します。同じカテゴリが何件あっても、返るのは 1 つだけです。

要件

  1. 関数名は used_categories で、引数は records の1つ
  2. 各支出の category を集め、set で重複を取り除く
  3. sorted でリストに変換してから return する。集合のまま返さない
  4. 空リストのときは空リストを返す

入出力例

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}])["交通費","娯楽"]

ヒント

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

メモ

setで重複除去

⌘S で保存