CHAPTER 2 ・ データ構造を使いこなす / Lesson 08
入れ子のデータを扱う
辞書の中にリスト、リストの中に辞書。JSONのような複雑な形を読み解き、平らな表に直します。
このレッスンが終わったら
「入れ子のデータは、外側から1段ずつ取り出せば読める。最後は『辞書のリスト』にすると、表(CSV・pandas)にしやすい」と言える。
箱の中に、箱が入っている
会計ソフトや受注システムから取り出したデータ(JSON)は、1行1行の表ではなく、箱の中に箱が入った形をしていることがよくあります。これを入れ子のデータと呼びます。下は、3件の注文が入った orders_nested.json です。
{
"count": 3,
"orders": [
{
"order_id": "O-1001",
"date": "2025-10-01",
"customer": {
"code": "C001",
"name": "アオイ電機株式会社",
"area": "東京"
},
"items": [
{
"sku": "P01",
"name": "ノートPC",
"qty": 2,
"price": 120000
},
{
"sku": "P02",
"name": "モニター",
"qty": 2,
"price": 35000
}
],
"memo": null
},
{
"order_id": "O-1002",
"date": "2025-10-03",
"customer": {
"code": "C005",
"name": "株式会社ヒマワリ食品",
"area": "福岡"
},
"items": [
{
"sku": "P05",
"name": "ミネラルウォーター(箱)",
"qty": 10,
"price": 1500
}
],
"memo": "午前中着希望"
},
{
"order_id": "O-1003",
"date": "2025-10-06",
"customer": {
"code": "C003",
"name": "株式会社サクラ印刷",
"area": "大阪"
},
"items": [
{
"sku": "P03",
"name": "業務ソフト",
"qty": 1,
"price": 48000
},
{
"sku": "P04",
"name": "保守サービス",
"qty": 12,
"price": 18000
},
{
"sku": "P02",
"name": "モニター",
"qty": 4,
"price": 35000
}
],
"memo": null
}
]
}
よく見ると、1件の注文(order_id が O-1001 など)の中に、得意先を表す customer(辞書)と、買った商品の一覧 items(リスト)が入っています。items の中の1つ1つは、また辞書です。
基礎編で学んだ json.load で読み込み、まず全体の姿を確かめましょう。
この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。
▶ 実行を押すと、ここに結果が出ます
いちばん外側は辞書で、キーは count と orders の2つです。orders は、注文を並べたリストで、3件入っています。ここから、「O-1001 の2つ目の商品は?」「注文ごとの合計金額は?」を知りたいとき、どう取り出せばよいのでしょうか。
外側から、1段ずつ取り出す
コツは、1段ずつ、外側から取り出すことです。取り出したものに名前を付けて、「今、何を持っているか」を print で確かめながら進みます。
この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。
▶ 実行を押すと、ここに結果が出ます
「注文の一覧」→「1件目の注文」→「その得意先」→「得意先名」と、1段ずつ降りていきました。慣れてきたら、[ ] を続けて、次のように1行で書くこともできます(結果は同じ「モニター」です)。
print(data["orders"][0]["items"][1]["name"])仕組みを読む
データの形を、図にしてみましょう。外側から内側へ、辞書とリストが交互に入っています。
- data(辞書)count と orders
- orders(リスト)注文が3件
- 注文1件(辞書)order_id・customer・items・memo
- items(リスト)明細が1〜3行
- 明細1行(辞書)sku・name・qty・price
1行で取り出した式は、この図を、左から右へたどった道順です。
dataいちばん外側(辞書)["orders"]辞書から、キーで取り出す → リスト[0]リストから、番号で取り出す → 1件目["items"]辞書から、キーで → 明細のリスト[1]リストから、番号で → 2行目["name"]辞書から、キーで → 商品名日本語にすると
見分け方は簡単で、辞書にはキー(["order_id"])、リストには番号([0])を使います。どちらか迷ったら、手前までを print して、{ } で始まれば辞書、[ ] で始まればリストだと確かめてください。
変えて、試す
① 全部の注文の、全部の明細を回す。 外側のリスト(注文)を for で回し、その中でもう一度 for(明細)で回します。for が二重になります。
この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。
▶ 実行を押すと、ここに結果が出ます
② 「ない」を安全に扱う。 memo(備考)は、注文によって None のことがあります。None に対して strip() のような文字の命令を使うと、エラーになります。
この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。
▶ 実行を押すと、ここに結果が出ます
AttributeError: 'NoneType' object has no attribute 'strip' と出ました。1件目は動き、2件目で止まっています。NoneType は「None のこと」です。対策は、None のときの代わりの値を決めておくことです。また、キーそのものがない場合は、Lesson 06 の get が使えます。
この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。
▶ 実行を押すと、ここに結果が出ます
order.get("memo") or "(なし)" は、「memo が None や空文字なら、代わりに『(なし)』を使う」という書き方です。customer には tel というキーがないので、get の2つ目の値「不明」が使われます。
③ 平らな「辞書のリスト」にする。 入れ子のままでは、Excelのような表にできません。明細1行ごとに、注文ID・得意先名・商品・数量・金額を1つの辞書にまとめ、リストに並べます。これを「平らにする」(フラット化)と呼びます。
この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。
▶ 実行を押すと、ここに結果が出ます
6件の明細が、同じ形の辞書として並びました。得意先名のように、外側の値は、明細の行ごとに繰り返し入れるのがポイントです。
④ pandas に渡す。CSVにも書き出せる。 「辞書のリスト」は、そのまま pd.DataFrame(...) に渡せます。
この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。
▶ 実行を押すと、ここに結果が出ます
自分で書く
お題① 注文ごとの合計金額(数量 × 単価の合計)を、{注文ID: 合計} の辞書にして表示してください。
この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。
▶ 実行を押すと、ここに結果が出ます
ヒントを見る
{order["order_id"]: sum(i["qty"] * i["price"] for i in order["items"]) for order in orders} の形です。sum の中の for が、明細を回しています。
答えの例を見る
import json
with open("orders_nested.json", encoding="utf-8") as f:
orders = json.load(f)["orders"]
totals = {
order["order_id"]: sum(i["qty"] * i["price"] for i in order["items"])
for order in orders
}
print(totals)お題② 全部の注文を合わせて、明細は全部で何行あるか、表示してください。
この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。
▶ 実行を押すと、ここに結果が出ます
ヒントを見る
sum(len(order["items"]) for order in orders) の形です。len は、リストの要素数を返します。
答えの例を見る
import json
with open("orders_nested.json", encoding="utf-8") as f:
orders = json.load(f)["orders"]
print(sum(len(order["items"]) for order in orders))お題③ 備考(memo)が入っている注文の、注文IDだけをリストにして表示してください。memo が None の注文は含めません。
この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。
▶ 実行を押すと、ここに結果が出ます
ヒントを見る
[order["order_id"] for order in orders if order.get("memo")] の形です。None は、if の条件では「ない」扱いになります。
答えの例を見る
import json
with open("orders_nested.json", encoding="utf-8") as f:
orders = json.load(f)["orders"]
ids = [order["order_id"] for order in orders if order.get("memo")]
print(ids)仕事ならどう使う?
理解チェックx = {"a": [{"b": 5}, {"b": 7}]} のとき、x["a"][1]["b"] の値は?
理解チェックJSON に "memo": null と書かれた注文を、Python に読み込んで order.get("memo") としたときの結果は?