CHAPTER 2 ・ データ構造を使いこなす / Lesson 08

入れ子のデータを扱う

辞書の中にリスト、リストの中に辞書。JSONのような複雑な形を読み解き、平らな表に直します。

目安 20分 ・ インストール不要

このレッスンが終わったら

「入れ子のデータは、外側から1段ずつ取り出せば読める。最後は『辞書のリスト』にすると、表(CSV・pandas)にしやすい」と言える。

STEP 1

箱の中に、箱が入っている

会計ソフトや受注システムから取り出したデータ(JSON)は、1行1行の表ではなく、箱の中に箱が入った形をしていることがよくあります。これを入れ子のデータと呼びます。下は、3件の注文が入った orders_nested.json です。

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 で読み込み、まず全体の姿を確かめましょう。

JSONを読んで、全体の形を見るサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

いちばん外側は辞書で、キーは count と orders の2つです。orders は、注文を並べたリストで、3件入っています。ここから、「O-1001 の2つ目の商品は?」「注文ごとの合計金額は?」を知りたいとき、どう取り出せばよいのでしょうか。

STEP 2

外側から、1段ずつ取り出す

コツは、1段ずつ、外側から取り出すことです。取り出したものに名前を付けて、「今、何を持っているか」を print で確かめながら進みます。

1段ずつ取り出すサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

「注文の一覧」→「1件目の注文」→「その得意先」→「得意先名」と、1段ずつ降りていきました。慣れてきたら、[ ] を続けて、次のように1行で書くこともできます(結果は同じ「モニター」です)。

print(data["orders"][0]["items"][1]["name"])
STEP 3

仕組みを読む

データの形を、図にしてみましょう。外側から内側へ、辞書とリストが交互に入っています。

  1. data(辞書)count と orders
  2. orders(リスト)注文が3件
  3. 注文1件(辞書)order_id・customer・items・memo
  4. items(リスト)明細が1〜3行
  5. 明細1行(辞書)sku・name・qty・price
入れ子の形:辞書とリストが、交互に入っている

1行で取り出した式は、この図を、左から右へたどった道順です。

data["orders"][0]["items"][1]["name"] の部品
dataいちばん外側(辞書)
["orders"]辞書から、キーで取り出す → リスト
[0]リストから、番号で取り出す → 1件目
["items"]辞書から、キーで → 明細のリスト
[1]リストから、番号で → 2行目
["name"]辞書から、キーで → 商品名

日本語にすると

「注文の一覧の、1件目の、明細の、2行目の、商品名」

見分け方は簡単で、辞書にはキー(["order_id"])、リストには番号([0])を使います。どちらか迷ったら、手前までを print して、{ } で始まれば辞書、[ ] で始まればリストだと確かめてください。

STEP 4

変えて、試す

① 全部の注文の、全部の明細を回す。 外側のリスト(注文)を for で回し、その中でもう一度 for(明細)で回します。for が二重になります。

二重の for で、明細を全部出すサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

② 「ない」を安全に扱う。 memo(備考)は、注文によって None のことがあります。None に対して strip() のような文字の命令を使うと、エラーになります。

わざと間違える:None に strip() を使うサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

AttributeError: 'NoneType' object has no attribute 'strip' と出ました。1件目は動き、2件目で止まっています。NoneType は「None のこと」です。対策は、None のときの代わりの値を決めておくことです。また、キーそのものがない場合は、Lesson 06 の get が使えます。

get と or で、安全に扱うサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

order.get("memo") or "(なし)" は、「memo が None や空文字なら、代わりに『(なし)』を使う」という書き方です。customer には tel というキーがないので、get の2つ目の値「不明」が使われます。

③ 平らな「辞書のリスト」にする。 入れ子のままでは、Excelのような表にできません。明細1行ごとに、注文ID・得意先名・商品・数量・金額を1つの辞書にまとめ、リストに並べます。これを「平らにする」(フラット化)と呼びます。

明細1行ずつの、平らな辞書のリストにするサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます

6件の明細が、同じ形の辞書として並びました。得意先名のように、外側の値は、明細の行ごとに繰り返し入れるのがポイントです。

④ pandas に渡す。CSVにも書き出せる。 「辞書のリスト」は、そのまま pd.DataFrame(...) に渡せます。

DataFrame にして、集計・保存するpandas・サンプルファイル付き
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
STEP 5

自分で書く

お題① 注文ごとの合計金額(数量 × 単価の合計)を、{注文ID: 合計} の辞書にして表示してください。

お題①:注文ごとの合計金額サンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

{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)

お題② 全部の注文を合わせて、明細は全部で何行あるか、表示してください。

お題②:明細の総数サンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

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 の注文は含めません。

お題③:備考のある注文のIDサンプルファイル付き・書き換えOK
Ctrl / ⌘ + Enter でも実行

この欄では、サンプルファイルが使えます。実行のたびに、ファイルは最初の状態に戻ります。

結果
▶ 実行を押すと、ここに結果が出ます
ヒントを見る

[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)
STEP 6

仕事ならどう使う?

理解チェックx = {"a": [{"b": 5}, {"b": 7}]} のとき、x["a"][1]["b"] の値は?

理解チェックJSON に "memo": null と書かれた注文を、Python に読み込んで order.get("memo") としたときの結果は?