コンテンツにスキップ

8-3. 演習問題


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

メソッドチェーンの処理を順に追う。

  1. filter(n => n % 2 !== 0):奇数のみを抽出 → [1, 3, 5]
  2. map(n => n * 10):各要素を10倍 → [10, 30, 50]

mapfilter はいずれも元の配列を変更せず、新しい配列を返す非破壊的メソッドだ。


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)ab が指す同じ配列を変更する。

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 = 0
1回目: acc = 0 + 400 = 400
2回目: acc = 400 + 600 = 1000
3回目: acc = 1000 + 350 = 1350

reduce は合計・最大値・グルーピングなど様々な集約処理に使える汎用的なメソッドだ。

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-1. mapfilterreduce の違いを、それぞれの「入力」と「出力」の観点で説明せよ。

解答欄
解答と解説
メソッド入力出力用途
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. 次の命令型スタイルのコードを、mapfilterreduce を使った宣言型スタイルに書き直せ。

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

下のブラウザプレイグラウンドでコードを完成させ、mapfilterreducesort の流れを確認せよ。

  1. 課題1の TODO を埋めて、tech ジャンルのタイトル一覧を出力する
  2. 課題2の TODO を埋めて、全本の平均価格を求める
  3. 課題3の TODO を埋めて、「3000円以上かつ tech」の本を価格の高い順に並べる
  4. console.log の出力が、自分の想定と一致するか確認する
実行プレイグラウンド
編集内容は自動保存されます / Ctrl+Enter でも実行できます
出力
「実行」を押すと、ここに結果が表示されます。
  • filter の条件を変えると、どの配列が残るか
  • sort が配列の順番にどう影響するか
  • reduce の初期値を 0 にする理由を説明できるか