JavaScript / LESSON 07 / JS07

JavaScript:オブジェクトとMap

1学習の目的

2基礎解説

やりたいこと書き方意味
オブジェクトを作る{ name: "田中", age: 30 }キー: 値 のペア
取り出すp.name / p["age"]番号ではなく名前で引く
安全に取り出すp.address?.city途中が無くてもエラーにしない
一覧を取るObject.entries(p)[キー, 値] の配列になる
✅ 覚えるべき重要ポイント
  1. 存在しないキーを引くと undefined が返り、エラーにならない。静かに undefined が混ざって計算が NaN になるのがJavaScriptの怖さ。
  2. 取り出しは p.name(ドット記法)が基本。キー名が変数に入っているときp[key](ブラケット記法)を使う。
  3. ループは for (const [key, value] of Object.entries(p))。キーだけなら Object.keys()、値だけなら Object.values()
  4. const { name, age } = person;まとめて取り出せる(分割代入)。= "未設定" と書けば既定値も指定できる。
  5. 数え上げの定番は count[key] = (count[key] || 0) + 1初回は undefined なので 0 として扱う——このイディオムは丸暗記してよい。

現場使用例:APIのJSON応答 res.data.items、設定値の管理、商品ごとの売上集計、フォームの入力値をまとめて保持。

const person = { name: "田中", age: 30 };

console.log(person.name);        // 田中
console.log(person.dept);        // undefined ← エラーにならない

person.dept = "営業";            // 追加
person.age = 31;                 // 上書き

for (const [key, value] of Object.entries(person)) {
  console.log(`${key}: ${value}`);
}

// 分割代入(まとめて取り出す)
const { name, age } = person;
console.log(name, age);          // 田中 31

// 安全に深い階層を取る
console.log(person.address?.city ?? "未登録");   // 未登録

// 数え上げの定番
const count = {};
for (const o of ["A", "B", "A"]) {
  count[o] = (count[o] || 0) + 1;
}
console.log(count);              // { A: 2, B: 1 }

3基本ドリル(10問)

JS07-D01 person = { name: "田中", age: 30 } から名前と年齢を、この順に1行ずつ出力せよ。 コーディング★☆☆無料
期待される結果

出力 == 田中 / 30(2行)

ヒント

ドットでつないで person.name のように書く。

模範解答
const person = { name: "田中", age: 30 };

console.log(person.name);
console.log(person.age);
解説

配列は番号、オブジェクトはキーで引く。person[0] とは書けない点に注意。

JS07-D02 d = { a: 1 } のとき d.b を実行するとどうなるか。
A. エラーになる B. undefined が返る C. null が返る D. 0 が返る
選択★☆☆無料
期待される結果

解答 == B

ヒント

JavaScriptは存在しないキーに寛容。

模範解答
B
解説

エラーで止まらないのが逆に危険。undefined のまま計算に使うと NaN になり、原因が離れた場所で発覚する。

JS07-D03 config = { theme: "light" } に fontSize = 14 を追加し、オブジェクト全体を出力せよ。 コーディング★☆☆無料
期待される結果

出力 == { theme: 'light', fontSize: 14 }

ヒント

config.fontSize = 14; と書くだけで追加される。

模範解答
const config = { theme: "light" };
config.fontSize = 14;
console.log(config);
解説

配列の push にあたる操作。代入するだけで追加されるので、追加と更新の書き方が同じ。キー名に日本語も使えるが、英数字にするのが実務の慣習。

JS07-D04 オブジェクトのキーと値をまとめて取り出すコードを完成させよ。 穴埋め★☆☆無料
コード
const scores = { 佐藤: 82, 鈴木: 95 };

for (const [name, score] of Object.____(scores)) {
  console.log(`${name}:${score}点`);
}
期待される結果

出力 == 佐藤:82点 / 鈴木:95点(2行)

ヒント

「項目」を意味する英単語。[キー, 値] のペアの配列を返す。

模範解答
entries
解説

Object.entries() は配列の entries() と同じ発想。JS05・JS06で使った分割代入 [a, b] がここでも活きる。

JS07-D05 obj に "age" というキーが存在するかを調べる正しい書き方はどれか。
A. obj.has("age") B. "age" in obj C. obj.includes("age") D. obj.exists("age")
選択★☆☆無料
期待される結果

解答 == B

ヒント

JS06で配列にも似た演算子を使った。

模範解答
B
解説

inキーを探す(値ではない)。includes は配列用、has は Map 用で、それぞれ対象が違う。

JS07-D06 employee = { name: "田中", dept: "営業", age: 30 } から、分割代入で name と dept を取り出し「田中(営業)」と出力せよ。 コーディング★★☆無料
期待される結果

出力 == 田中(営業)

ヒント

const { name, dept } = employee; と書くと2つ同時に取り出せる。

模範解答
const employee = { name: "田中", dept: "営業", age: 30 };

const { name, dept } = employee;

console.log(`${name}(${dept})`);
解説

変数名とキー名が一致していれば自動で対応する。必要な項目だけ取り出せるので、APIの応答から欲しいデータだけ抜くときに多用する。

JS07-D07 address が無くてもエラーにならず「未登録」と表示されるコードを完成させよ。 穴埋め★★☆無料
コード
const user = { name: "田中" };
console.log(user.address____city ?? "未登録");
期待される結果

出力 == 未登録

ヒント

「もし存在すれば続ける」を表す記号。クエスチョンマークとドットを組み合わせる。

模範解答
?.
解説

オプショナルチェーン。これが無いと Cannot read properties of undefined というエラーで止まる。APIの応答を扱うときの必需品。

JS07-D08 d = { a: 1 } に対して d.a = 5 を実行するとどうなるか。
A. エラーになる B. 値が5に上書きされる C. キーが2つになる D. 無視される
選択★★☆無料
期待される結果

解答 == B

ヒント

同じキーを重複して持てるか?

模範解答
B
解説

キーは重複できないので、同じキーへの代入は常に上書き。これを利用して「最新の値で更新する」処理が書ける。

JS07-D09 orders = ["A", "B", "A", "C", "B", "A"] の出現回数をオブジェクトで数え、そのまま出力せよ。 コーディング★★☆無料
期待される結果

出力 == { A: 3, B: 2, C: 1 }

ヒント

空のオブジェクト count = {} を用意し、count[o] = (count[o] || 0) + 1 で数え上げる。

模範解答
const orders = ["A", "B", "A", "C", "B", "A"];

const count = {};

for (const o of orders) {
  count[o] = (count[o] || 0) + 1;
}

console.log(count);
解説

(count[o] || 0) + 1 は集計の最重要イディオム。初回は undefined なので || 0 で 0 に置き換えている。JS06で二重ループになった重複カウントが、1回のループで書けるようになった。

JS07-D10 scores = { 佐藤: 82, 鈴木: 95, 高橋: 67 } の値だけの合計を出力せよ。 コーディング★★☆無料
期待される結果

出力 == 244

ヒント

Object.values() で値の配列が取れる。あとは for...of で足し込む。

模範解答
const scores = { 佐藤: 82, 鈴木: 95, 高橋: 67 };

let total = 0;

for (const s of Object.values(scores)) {
  total += s;
}

console.log(total);
解説

Object.values()ただの配列を返すので、JS06で学んだ配列操作がすべて使える。

4実践シナリオ(5問)

JS07-S01 【社員情報の表示】employee = { name: "田中", dept: "営業", age: 30 } の全項目を「name: 田中」の形式で出力し、最後に「項目数:3」と出力せよ。 コーディング★★☆無料
期待される結果

3行の項目+最終行 項目数:3

ヒント

Object.entries() で回す。項目数は Object.keys(employee).length。

模範解答
const employee = { name: "田中", dept: "営業", age: 30 };

for (const [key, value] of Object.entries(employee)) {
  console.log(`${key}: ${value}`);
}

console.log(`項目数:${Object.keys(employee).length}`);
解説

オブジェクトには .length が無いので、Object.keys() で配列にしてから length を取る。APIの応答の中身をとりあえず全部見たいときにこの形をよく使う。

JS07-S02 【成績表・オブジェクト版】scores = { 佐藤: 82, 鈴木: 95, 高橋: 67 } を「佐藤:82点」の形式で出力し、最後に「平均:81.3点」と小数1桁で出力せよ。 コーディング★★☆無料
期待される結果

3行の成績+最終行 平均:81.3点

ヒント

Object.entries() で回す。平均は Object.values() の合計を件数で割り、toFixed(1) で小数1桁にする。

模範解答
const scores = { 佐藤: 82, 鈴木: 95, 高橋: 67 };

for (const [name, score] of Object.entries(scores)) {
  console.log(`${name}:${score}点`);
}

const values = Object.values(scores);
let total = 0;
for (const v of values) {
  total += v;
}

console.log(`平均:${(total / values.length).toFixed(1)}点`);
解説

JS06のS02では names と scores の2本を番号でひも付けたが、オブジェクトなら1本で完結し順序ズレも起きない。同じ問題の改良版として見比べてほしい。

JS07-S03 【在庫チェック】stock = { ボールペン: 120, ノート: 8, 付箋: 67 }、threshold = 10。在庫が threshold 未満の商品を「⚠ノート:8個」の形式で出力し、最後に「要発注:1品目」と出力せよ。 コーディング★★☆無料
期待される結果

該当1件を出力後、最終行 要発注:1品目

ヒント

Object.entries() で回して if で判定。カウンタはループの前に用意する。

模範解答
const stock = { ボールペン: 120, ノート: 8, 付箋: 67 };
const threshold = 10;

let count = 0;

for (const [name, num] of Object.entries(stock)) {
  if (num < threshold) {
    console.log(`⚠${name}:${num}個`);
    count++;
  }
}

console.log(`要発注:${count}品目`);
解説

JS06の在庫レポートをオブジェクトで書き直した形。商品名と在庫数が1つにまとまっているので、indexOf で引く必要がなくなった。

JS07-S04 【商品別売上集計】sales = [["A", 1200], ["B", 800], ["A", 500], ["C", 300], ["B", 1000]] から商品ごとの合計を求め、「A:1,700円」の形式で出力し、最後に「合計:3,800円」と出力せよ。 コーディング★★★無料
期待される結果

商品別3行+最終行 合計:3,800円

ヒント

totals[name] = (totals[name] || 0) + amount で足し込む。配列の要素は [name, amount] の形なので分割代入で受け取れる。

模範解答
const sales = [["A", 1200], ["B", 800], ["A", 500], ["C", 300], ["B", 1000]];

const totals = {};

for (const [name, amount] of sales) {
  totals[name] = (totals[name] || 0) + amount;
}

let sum = 0;

for (const [name, amount] of Object.entries(totals)) {
  console.log(`${name}:${amount.toLocaleString()}円`);
  sum += amount;
}

console.log(`合計:${sum.toLocaleString()}円`);
解説

D09の数え上げを加算に変えただけで売上集計になる。配列の配列(JS06)+オブジェクト集計(JS07)の合わせ技で、実務のデータ処理の大半がカバーできる。

JS07-S05 【APIレスポンスの解析】次のオブジェクトを解析せよ。
const res = { status: 200, data: { users: [{ name: "田中", age: 30 }, { name: "鈴木", age: 25 }] } };
「ステータス:200」「ユーザー数:2人」「田中(30歳)」「鈴木(25歳)」「平均年齢:27.5歳」の5行を出力すること。
コーディング★★★無料
期待される結果

出力5行が完全一致

ヒント

res.data.users で階層をたどる。ユーザーは for...of で回し、分割代入 { name, age } で取り出せる。

模範解答
const res = { status: 200, data: { users: [{ name: "田中", age: 30 }, { name: "鈴木", age: 25 }] } };

const users = res.data.users;

console.log(`ステータス:${res.status}`);
console.log(`ユーザー数:${users.length}人`);

let totalAge = 0;

for (const { name, age } of users) {
  console.log(`${name}(${age}歳)`);
  totalAge += age;
}

console.log(`平均年齢:${totalAge / users.length}歳`);
解説

APIの応答はほぼこの形(オブジェクトの中に配列、配列の中にオブジェクト)for (const { name, age } of users) のようにループの受け取り側で直接分割代入できるのがJavaScriptの便利なところ。

5仕上げ課題

JS07-FINAL 【アンケート集計レポート】回答データから2種類の集計を出力せよ。

const responses = [
 { area: "東京", rating: "満足" },
 { area: "大阪", rating: "普通" },
 { area: "東京", rating: "満足" },
 { area: "福岡", rating: "不満" },
 { area: "大阪", rating: "満足" },
 { area: "東京", rating: "普通" },
 { area: "東京", rating: "満足" },
];


仕様:
① 地域別の件数を「東京:4件」の形式で出力([地域別] の見出し付き)
② 評価別の件数を同じ形式で出力([評価別] の見出し付き)
③ どちらも最初に登場した順で並べること
④ 回答数と地域数、満足率(小数1桁)を出力

出力仕様(12行):
=== アンケート集計 ===
[地域別]
東京:4件
大阪:2件
福岡:1件
[評価別]
満足:4件
普通:2件
不満:1件
---
回答数:7件 / 地域数:3
満足率:57.1%
期待される結果

出力12行が完全一致

ヒント

地域用と評価用の空オブジェクトを2つ用意し、1回のループで両方を (obj[key] || 0) + 1 で数え上げる。文字列キーは追加した順が保たれるので並べ替えは不要。満足率は 満足の件数 / 回答数 * 100 を toFixed(1) で。

模範解答
const responses = [
  { area: "東京", rating: "満足" },
  { area: "大阪", rating: "普通" },
  { area: "東京", rating: "満足" },
  { area: "福岡", rating: "不満" },
  { area: "大阪", rating: "満足" },
  { area: "東京", rating: "普通" },
  { area: "東京", rating: "満足" },
];

const areaCount = {};
const ratingCount = {};

for (const { area, rating } of responses) {
  areaCount[area] = (areaCount[area] || 0) + 1;
  ratingCount[rating] = (ratingCount[rating] || 0) + 1;
}

console.log("=== アンケート集計 ===");

console.log("[地域別]");
for (const [area, num] of Object.entries(areaCount)) {
  console.log(`${area}:${num}件`);
}

console.log("[評価別]");
for (const [rating, num] of Object.entries(ratingCount)) {
  console.log(`${rating}:${num}件`);
}

const rate = (ratingCount["満足"] / responses.length) * 100;

console.log("---");
console.log(`回答数:${responses.length}件 / 地域数:${Object.keys(areaCount).length}`);
console.log(`満足率:${rate.toFixed(1)}%`);
解説

1回のループで2つの軸を同時に集計するのが要点。データを2周する必要はない。これはExcelのピボットテーブルに相当する処理で、ここが書ければ集計業務はほぼ自動化できる。

③の「登場順」が成立するのは、文字列キーは追加した順序が保たれるというJavaScriptの仕様のおかげ。ただし注意点があり、キーが数字だと小さい順に自動で並び替わる{ b: 1, a: 2, 10: 3, 2: 4 }{ 2: 4, 10: 3, b: 1, a: 2 } になる。年度別集計などで順序が狂ったら、これを疑うとよい。

次章では関数を学ぶ。ここまでに書いた集計処理を部品として切り出し、何度でも呼び出せるようになる。