JavaScript | reduce と map/filter を組み合わせた応用パターン

JavaScript JavaScript
スポンサーリンク

ここでは、reducemapfilter を組み合わせた実践的な使い方+パフォーマンス面の注意
を、初心者向けに かみ砕いて+コード例つき で解説します。


目標

単純な合計だけでなく、
「条件でしぼる → 加工する → 集計する」
という一連の流れを、mapfilterreduce を組み合わせてスマートに書けるようになる。


まずおさらい:それぞれの役割

メソッド役割結果の形
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

💬 解説:

  1. filter() で条件を満たす要素だけを取り出す
  2. 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
JavaScript

2️⃣ ステップごとの動き(初心者向け解説)

ステップ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

ポイントまとめ

  1. filter → 条件に合う要素だけ残す
  2. map → 必要な値だけ抽出する
  3. reduce → 配列を1つの値にまとめる(ここでは合計)
  4. 平均は「合計 ÷ 要素数」で計算

💡 さらに効率的に書く方法(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
JavaScript
  • reduce 1回で「合計+人数」をまとめて計算
  • 配列のループ回数を減らせる → 大量データでパフォーマンス向上

では、先ほどの「点数80以上の人の平均点」を求める処理を ステップごとに表形式で視覚化 します。
各ステップで配列がどう変化しているかが一目でわかるように整理しました。


配列の変化ステップ表

ステップ処理内容コード配列の状態
0元の配列data[ {A,90}, {B,70}, {C,85}, {D,60} ]
180点以上をフィルタ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.lengthaverage = 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/count87.5

💡 この表を意識すると、filter → map → reducereduce 1回でまとめる方法挙動の違いとステップの流れ が一目で理解できます。

タイトルとURLをコピーしました