8-3. 演習問題
問題1:選択問題
Section titled “問題1:選択問題”1-1. 次のコードの result の値はどれか。
const nums = [1, 2, 3, 4, 5];const result = nums.filter(n => n % 2 !== 0).map(n => n * 10);- A.
[10, 20, 30, 40, 50] - B.
[10, 30, 50] - C.
[2, 4] - D.
[20, 40]
解答と解説
正解:B
メソッドチェーンの処理を順に追う。
filter(n => n % 2 !== 0):奇数のみを抽出 →[1, 3, 5]map(n => n * 10):各要素を10倍 →[10, 30, 50]
map と filter はいずれも元の配列を変更せず、新しい配列を返す非破壊的メソッドだ。
1-2. 次のコードの出力結果はどれか。
const a = [1, 2, 3];const b = a;b.push(4);console.log(a.length);- A.
3 - B.
4 - C.
undefined - D. エラーが発生する
解答と解説
正解:B
配列はオブジェクト型のため、const b = a は配列をコピーするのではなく、同じ配列への参照をコピーする。したがって b.push(4) は a と b が指す同じ配列を変更する。
a ──→ [1, 2, 3]b ──→ [1, 2, 3] ← a と b は同じ配列を指している本当にコピーしたい場合はスプレッド構文を使う:
const b = [...a]; // 新しい配列を作る(浅いコピー)b.push(4);console.log(a.length); // 3(元の配列は変わらない)1-3. 次のオブジェクトの分割代入の結果として正しいものはどれか。
const { x: a = 10, y: b = 20 } = { x: 5 };- A.
a = 10,b = 20 - B.
a = 5,b = 20 - C.
x = 5,y = undefined - D.
a = 5,b = undefined
解答と解説
正解:B
{ x: a = 10 } は「x プロパティを取り出して変数 a に代入し、x が存在しない場合のデフォルト値は 10」という意味だ。
オブジェクトに x: 5 があるため a = 5 になる。y プロパティは存在しないため、デフォルト値 20 が使われ b = 20 になる。
変数名と取り出すキー名が混同しやすいので整理すると:
{ オブジェクトのキー: 変数名 = デフォルト値 }{ x: a = 10 }1-4. 次のコードで total の値はどれか。
const orders = [ { item: "coffee", price: 400 }, { item: "cake", price: 600 }, { item: "juice", price: 350 },];const total = orders.reduce((acc, order) => acc + order.price, 0);- A.
400 - B.
1000 - C.
1350 - D.
NaN
解答と解説
正解:C
reduce は配列を1つの値に集約するメソッドだ。acc(アキュムレータ)は初期値 0 から始まり、各 order.price を加算していく。
初期値: acc = 01回目: acc = 0 + 400 = 4002回目: acc = 400 + 600 = 10003回目: acc = 1000 + 350 = 1350reduce は合計・最大値・グルーピングなど様々な集約処理に使える汎用的なメソッドだ。
問題2:穴埋め問題
Section titled “問題2:穴埋め問題”2. 次のコードの( )に入るコードを答えよ。
const users = [ { name: "Alice", score: 85 }, { name: "Bob", score: 92 }, { name: "Carol", score: 78 },];
// (1) スコアが80以上のユーザーの名前だけを配列で取得するconst highScorers = users .(1)(u => u.score >= 80) .(1b)(u => u.name);// ["Alice", "Bob"]
// (2) オブジェクトをスプレッド構文でコピーし、score を更新するconst updated = { ...users[0], (2): 90 };// { name: "Alice", score: 90 }
// (3) 配列の分割代入で最初の要素と残りを分けるconst [(3a), ...(3b)] = [10, 20, 30, 40];// first = 10, rest = [20, 30, 40]解答と解説
- (1)
filter - (1b)
map - (2)
score - (3a)
first(任意の変数名) - (3b)
rest(任意の変数名)
解説
(1) filter で条件に合う要素を絞り込み、map で名前だけを取り出すメソッドチェーン。
(2) スプレッド構文 { ...obj, key: value } は「オブジェクトの全プロパティをコピーした上で、指定したキーを上書きした新しいオブジェクト」を作る。Reactのstateの更新でも多用されるパターン。
(3) [first, ...rest] の ... は残余要素で、最初の要素以外を配列としてまとめる。
問題3:記述問題
Section titled “問題3:記述問題”3-1. map・filter・reduce の違いを、それぞれの「入力」と「出力」の観点で説明せよ。
解答と解説
| メソッド | 入力 | 出力 | 用途 |
|---|---|---|---|
map | 配列 | 同じ長さの新しい配列 | 各要素を変換する |
filter | 配列 | 条件を満たす要素の新しい配列(長さが変わる) | 要素を絞り込む |
reduce | 配列 | 単一の値(配列でなくてよい) | 配列を集約する |
[1, 2, 3].map(n => n * 2) // [2, 4, 6](長さ3→3)[1, 2, 3].filter(n => n > 1) // [2, 3] (長さ3→2)[1, 2, 3].reduce((a, n) => a + n, 0) // 6 (配列→数値)実務での選択基準:
- 各要素を加工したい →
map - 条件に合う要素だけ欲しい →
filter - 合計・最大値・グルーピングなど集約したい →
reduce
3-2. 次の命令型スタイルのコードを、map・filter・reduce を使った宣言型スタイルに書き直せ。
const products = [ { name: "apple", price: 150, category: "fruit" }, { name: "banana", price: 80, category: "fruit" }, { name: "milk", price: 200, category: "dairy" }, { name: "orange", price: 120, category: "fruit" },];
// フルーツカテゴリの商品価格の合計を求めるlet total = 0;for (let i = 0; i < products.length; i++) { if (products[i].category === "fruit") { total += products[i].price; }}解答と解説
const total = products .filter(p => p.category === "fruit") .reduce((acc, p) => acc + p.price, 0);// 150 + 80 + 120 = 350宣言型スタイルは「何をするか」を記述し、「どうやって処理するか(ループのインデックス管理など)」を隠蔽する。コードが短く読みやすくなる上、バグも入り込みにくい。
さらに map を組み合わせることもできる:
const total = products .filter(p => p.category === "fruit") .map(p => p.price) // 価格だけの配列を作る .reduce((a, p) => a + p, 0);問題4:ハンズオン
Section titled “問題4:ハンズオン”下のブラウザプレイグラウンドでコードを完成させ、map・filter・reduce・sort の流れを確認せよ。
取り組む内容
Section titled “取り組む内容”- 課題1の TODO を埋めて、
techジャンルのタイトル一覧を出力する - 課題2の TODO を埋めて、全本の平均価格を求める
- 課題3の TODO を埋めて、「3000円以上かつ tech」の本を価格の高い順に並べる
console.logの出力が、自分の想定と一致するか確認する
確認したいポイント
Section titled “確認したいポイント”filterの条件を変えると、どの配列が残るかsortが配列の順番にどう影響するかreduceの初期値を0にする理由を説明できるか