ここでは、reduce と map/filter を組み合わせた実践的な使い方+パフォーマンス面の注意
を、初心者向けに かみ砕いて+コード例つき で解説します。
目標
単純な合計だけでなく、
「条件でしぼる → 加工する → 集計する」
という一連の流れを、map・filter・reduce を組み合わせてスマートに書けるようになる。
まずおさらい:それぞれの役割
| メソッド | 役割 | 結果の形 |
|---|---|---|
map() | 各要素を「変換」する | 配列 |
filter() | 条件に「合うものだけ」残す | 配列 |
reduce() | 配列全体を「1つの値」にまとめる | 値(数値・文字列・オブジェクトなど) |
例1:条件付き合計(filter → reduce)
やりたいこと
「点数が80点以上の人だけを合計したい」
const students = [
{name: 'Yamada', score: 75},
{name: 'Suzuki', score: 91},
{name: 'Kudou', score: 80},
{name: 'Sato', score: 65}
];
// 80点以上だけフィルタ → 合計
const total = students
.filter(s => s.score >= 80)
.reduce((sum, s) => sum + s.score, 0);
console.log(total); // 171
JavaScript💬 解説:
filter()で条件を満たす要素だけを取り出すreduce()でそれらの合計を求める
例2:map + reduce で平均を出す
やりたいこと
「点数だけ取り出して、その平均を出す」
const students = [
{name: 'Yamada', score: 75},
{name: 'Suzuki', score: 91},
{name: 'Kudou', score: 80}
];
const scores = students.map(s => s.score); // [75, 91, 80]
const avg = scores.reduce((sum, n) => sum + n, 0) / scores.length;
console.log(avg); // 82
JavaScript💬 map() で取り出した「数値配列」を、reduce() でまとめるという流れ。
例3:map + filter + reduce(実務的)
やりたいこと
「APIから取得した注文データを集計し、
“発送済み”の注文だけの合計金額を出したい」
const orders = [
{id: 1, status: 'shipped', items: [{price: 1000}, {price: 2000}]},
{id: 2, status: 'pending', items: [{price: 500}]},
{id: 3, status: 'shipped', items: [{price: 800}, {price: 1200}]}
];
// 1️⃣ 発送済みだけ残す
// 2️⃣ items の配列を展開して価格だけに変換
// 3️⃣ reduce で合計
const totalPrice = orders
.filter(order => order.status === 'shipped')
.map(order => order.items) // [[{price:1000},{price:2000}], [{price:800},{price:1200}]]
.flat() // [{price:1000}, {price:2000}, {price:800}, {price:1200}]
.reduce((sum, item) => sum + item.price, 0);
console.log(totalPrice); // 5000
JavaScript🧩 ここでは filter → map → flat → reduce の流れ。flat()(配列の配列を1段階だけ結合)を入れることで、構造をきれいに整えています。
例4:reduce だけで全部やる(ワンライナー的)
上の例を「filter + map」せずに reduce だけで書くこともできます。
const totalPrice = orders.reduce((sum, order) => {
if (order.status === 'shipped') {
const subTotal = order.items.reduce((s, i) => s + i.price, 0);
return sum + subTotal;
}
return sum;
}, 0);
console.log(totalPrice); // 5000
JavaScript📘 こうするとループは一度で済む(後述のパフォーマンスに関係)。
パフォーマンスの注意点
| 書き方 | ループ回数 | 説明 |
|---|---|---|
filter → map → reduce | 配列を3回ループ | 可読性は高いがやや非効率 |
reduce 1回でまとめる | 配列を1回ループ | 処理は速いが可読性が落ちる |
🧩 目安
- 配列が数百~数千件程度なら
filter → map → reduceで十分。 - 数十万件以上を扱う場合は、
reduceひとつでまとめる方が良い。
コーディングの考え方
- 読みやすさ優先:まずは
filter → map → reduceで書いてみる - 速度が問題になったら
reduceひとつにまとめる reduceは「for文に似た柔軟さをもつ map/filter の上位互換」だが、
むやみに使うと可読性が落ちることを忘れずに!
練習問題
次の配列から「点数80以上の人の平均点」を map, filter, reduce を使って求めてみましょう 👇
const data = [
{name: "A", score: 90},
{name: "B", score: 70},
{name: "C", score: 85},
{name: "D", score: 60}
];
JavaScriptでは、この問題:
配列
[{name: "A", score: 90}, {name: "B", score: 70}, {name: "C", score: 85}, {name: "D", score: 60}]から、点数80以上の人の平均点を求める
の 解答+ステップごとの動き を、初心者向けにわかりやすく整理します。
1️⃣ 解答(filter + map + reduce で)
const data = [
{name: "A", score: 90},
{name: "B", score: 70},
{name: "C", score: 85},
{name: "D", score: 60}
];
// 80以上の点数だけ取り出す
const highScores = data.filter(student => student.score >= 80);
// 点数だけの配列に変換
const scores = highScores.map(student => student.score);
// 合計を求めて平均を計算
const total = scores.reduce((sum, n) => sum + n, 0);
const average = total / scores.length;
console.log(average); // 87.5
JavaScript2️⃣ ステップごとの動き(初心者向け解説)
ステップ0:元の配列
[
{A, 90}, {B, 70}, {C, 85}, {D, 60}
]
ステップ1:80点以上でフィルタ(filter)
const highScores = data.filter(student => student.score >= 80);
JavaScript- 条件:
student.score >= 80 - 結果:
[
{A, 90}, {C, 85}
]
ステップ2:点数だけ抽出(map)
const scores = highScores.map(student => student.score);
JavaScript- 各オブジェクトの
scoreを取り出す - 結果:
[90, 85]
ステップ3:合計を計算(reduce)
const total = scores.reduce((sum, n) => sum + n, 0);
JavaScript- 初期値:
sum = 0 - 1回目: sum=0, n=90 → sum becomes 90
- 2回目: sum=90, n=85 → sum becomes 175
結果:
total = 175
ステップ4:平均を求める
const average = total / scores.length;
JavaScript- scores.length = 2
- 175 ÷ 2 = 87.5
結果:
average = 87.5
✅ ポイントまとめ
filter→ 条件に合う要素だけ残すmap→ 必要な値だけ抽出するreduce→ 配列を1つの値にまとめる(ここでは合計)- 平均は「合計 ÷ 要素数」で計算
💡 さらに効率的に書く方法(reduce1回でまとめる)
const average = data.reduce((acc, student) => {
if (student.score >= 80) {
acc.sum += student.score;
acc.count += 1;
}
return acc;
}, {sum: 0, count: 0});
const result = average.sum / average.count;
console.log(result); // 87.5
JavaScriptreduce1回で「合計+人数」をまとめて計算- 配列のループ回数を減らせる → 大量データでパフォーマンス向上
では、先ほどの「点数80以上の人の平均点」を求める処理を ステップごとに表形式で視覚化 します。
各ステップで配列がどう変化しているかが一目でわかるように整理しました。
配列の変化ステップ表
| ステップ | 処理内容 | コード | 配列の状態 |
|---|---|---|---|
| 0 | 元の配列 | data | [ {A,90}, {B,70}, {C,85}, {D,60} ] |
| 1 | 80点以上をフィルタ | data.filter(student => student.score >= 80) | [ {A,90}, {C,85} ] |
| 2 | 点数だけ抽出 | highScores.map(student => student.score) | [ 90, 85 ] |
| 3 | 合計を計算 | scores.reduce((sum,n)=>sum+n,0) | total = 175 |
| 4 | 平均を計算 | total / scores.length | average = 87.5 |
補足:1回の reduce でまとめた場合の変化
| ステップ | 処理内容 | コード | 累積オブジェクトの状態 |
|---|---|---|---|
| 0 | 初期値設定 | {sum:0,count:0} | {sum:0, count:0} |
| 1 | 学生A (score=90) を処理 | if(score>=80) | {sum:90, count:1} |
| 2 | 学生B (score=70) を処理 | 条件を満たさないのでスキップ | {sum:90, count:1} |
| 3 | 学生C (score=85) を処理 | 条件を満たす | {sum:175, count:2} |
| 4 | 学生D (score=60) を処理 | 条件を満たさないのでスキップ | {sum:175, count:2} |
| 5 | 平均を計算 | sum/count | 87.5 |
💡 この表を意識すると、filter → map → reduce と reduce 1回でまとめる方法 の 挙動の違いとステップの流れ が一目で理解できます。

