キーで join (map 利用)
注文のデータに、名前は書かれていない
注文 1 件ごとに持っているのは user_id という番号だけで、名前は入っていません。手を抜いたわけではなく、名前を注文側にも書き写すと、改名が起きたときに過去の注文を全部直す羽目になるからです。名前は 1 か所に置いて、注文側は番号でそこを指す。この「別の一覧の目印を指しているだけの値」を外部キーと呼びます。
そのぶん、画面に名前を出すときは取りに行く必要があります。素直に書くと、注文 1 件ごとにユーザー一覧を先頭から見ることになります。
Python
for order in orders:
for user in users:
if user["user_id"] == order["user_id"]:
print(user["name"], order["amount"])動きはします。テストも通ります。ただし、ユーザーが 10 万人、注文が 100 万件になると比較の回数は掛け算で効いてきて、1000 億回に届きます。第 3 章で見た二重ループの重さがそのまま出てくる形です。
探すのをやめて、先に引ける形を作る
内側のループは「この番号の人は誰か」を毎回ゼロから調べ直しています。一度調べた結果を捨てているのが無駄です。番号から名前を引ける表を先に 1 回だけ作っておけば、あとは一発で引けます。
内線番号で言えば、こういうことをしておく形です。
Python
book = {}
for name, ext in [["佐藤", 1201], ["鈴木", 1204]]:
book[name] = ext
book["鈴木"] # 1204辞書は、中に何件入っていても 1 件を引く時間がほぼ変わりません。作るのに n 回、引くのに m 回で、合計は n + m に収まります。100 万件でも待たされません。データベースが JOIN を実行するときも、内部ではこれと同じことをしています。hash join と呼ばれる古典的な手です。
無い番号を引いたときの振る舞いが、言語で違う
Python
book["田中"] # KeyError: '田中'Python はその場で落ちます。JavaScript は undefined を返して黙って進むので、名前の欄が空のまま画面に出ます。落ちるほうが親切なこともあれば、止まると困ることもあります。
今回の課題では、対応するユーザーが必ず存在する前提でかまいません。ただし実務では、退会したユーザーの注文だけが残っている状態は普通に起きます。既定値を入れるのか、その行を出さないのかを決めておいてください。
やってみよう
joinOrders(users, orders) を完成させてください。
usersから、user_idを渡すとnameが返る辞書(JavaScript なら Object)を 1 回だけ作るordersを 1 周し、各注文のuser_idでその辞書を引いて名前を得るnameorder_idamountの 3 つを持つ形にして並べる。並び順はordersのまま変えない
冒頭の二重ループでもテストは通ります。通ってしまうので、通ったあとに「辞書を先に作る形になっているか」を自分で見直してください。
要件
- users から
user_idをキー、nameを値とするマップ(dict / Object)を作る - orders の各要素から user_id で name を引いて { name, order_id, amount } を生成する
- 出力の並び順は入力 orders の順番を維持する
入出力例
joinOrders([{"name":"Alice","user_id":1},{"name":"Bob","user_id":2}], [{"amount":1200,"order_id":101,"user_id":1},{"amount":800,"order_id":102,"user_id":2}]) → [{"amount":1200,"name":"Alice","order_id":101},{"amount":800,"name":"Bob","order_id":102}]
joinOrders([{"name":"Alice","user_id":1}], [{"amount":1200,"order_id":101,"user_id":1},{"amount":500,"order_id":103,"user_id":1}]) → [{"amount":1200,"name":"Alice","order_id":101},{"amount":500,"name":"Alice","order_id":103}]
joinOrders([{"name":"Alice","user_id":1},{"name":"Bob","user_id":2},{"name":"Carol","user_id":3}], [{"amount":700,"order_id":201,"user_id":3},{"amount":300,"order_id":202,"user_id":1},{"amount":1500,"order_id":203,"user_id":2}]) → [{"amount":700,"name":"Carol","order_id":201},{"amount":300,"name":"Alice","order_id":202},{"amount":1500,"name":"Bob","order_id":203}]
joinOrders([{"name":"Alice","user_id":1}], []) → []