Array.reduce
Array.reduce とは
reduce で配列を 1 つの値にまとめる。本レッスンでは、Array.reduce の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
reduce で 1 つの値にまとめる
map は加工、 filter は絞り込み、そして reduce は 集約 を担当します。 Array.prototype.reduce を使うと、配列を 合計 、 最大値 、 平均 、 オブジェクトへの集約 などに変換できます。配列メソッドの中で最も汎用的で、最初は少し戸惑うけれど、慣れると何にでも使える万能ツールです。
reduceは配列を1 つの値にまとめるためのメソッドです。合計だけでなく、配列をオブジェクトに変換、グループ化などにも使えます。
map や filter は新しい配列を返しますが、 reduce は数値・文字列・オブジェクトなど 何でも 返せます。これが reduce を強力にしている理由です。
reduce の文法
書き方は次のとおりです。
JavaScript
const nums = [1, 2, 3, 4];
const total = nums.reduce((acc, n) => acc + n, 0);
console.log(total); // 10reduce の引数は 2 つあります。 コールバック と 初期値 です。コールバックは (acc, current) の 2 引数で、 acc は今までの集計結果、 current は今の要素です。コールバックの戻り値が次の acc になります。
初期値を渡さないこともできますが、空配列で例外になるので 常に初期値を渡す のが安全です。
JavaScript
// 初期値あり — 安全
const safe = [].reduce((acc, n) => acc + n, 0); // 0
// 初期値なし — 例外!
// const unsafe = [].reduce((acc, n) => acc + n);
// TypeError: Reduce of empty array with no initial value
reduceのコールバックは(acc, current, index, array)の 4 引数まで取れますが、ほとんどのケースで前 2 つだけで十分です。
動きを追ってみる
合計を求める処理は内部的にこんな感じで進みます。
ひとつずつ要素を取り出して acc を更新していく — それだけです。 for で書けば同じことができますが、 reduce を使うと処理の意図がコールバック式 1 行に凝縮されます。
色々な集計に使える
reduce は合計だけのものではありません。
JavaScript
// 最大値
const nums = [3, 1, 4, 1, 5, 9, 2, 6];
const max = nums.reduce((acc, n) => (n > acc ? n : acc), -Infinity);
console.log(max); // 9
// 文字列を連結
const words = ['Hello', 'World'];
const sentence = words.reduce((acc, w) => `${acc} ${w}`, '').trim();
console.log(sentence); // 'Hello World'
// 配列からオブジェクトへ
const pairs = [['a', 1], ['b', 2], ['c', 3]];
const obj = pairs.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});
console.log(obj); // { a: 1, b: 2, c: 3 }
reduceは万能ですが、合計や平均のような単純な集計だけなら、もっとシンプルなメソッドやforでも構いません。読みやすさを優先して使い分けましょう。
map と filter を組み合わせる
reduce 単体で使うこともありますが、 map 、 filter と組み合わせて 処理のパイプライン を作ると JavaScript らしいコードになります。
JavaScript
const nums = [1, 2, 3, 4, 5, 6];
const sumOfEvenSquares = nums
.filter((n) => n % 2 === 0) // [2, 4, 6]
.map((n) => n * n) // [4, 16, 36]
.reduce((acc, n) => acc + n, 0); // 56
console.log(sumOfEvenSquares);偶数だけ を 絞り込み 、 二乗 に 加工 し、 合計 で 集約 、という流れが上から下に読めます。 関数型プログラミング の入口です。
よくある間違い
以下のとおりです。
- 初期値を忘れて空配列で例外を出す
- コールバックで
returnを書き忘れて、accがundefinedになっていく accを直接ミューテーションしてしまう (オブジェクトを返すときは{ ...acc, ... }で新しいオブジェクトを返すと安全)
JavaScript
// NG — return を書き忘れている
const bad = [1, 2, 3].reduce((acc, n) => { acc + n }, 0);
console.log(bad); // undefinedアロー関数で
{}を使うときはreturnが必要 — このルールはreduceのコールバックでも変わりません。
やってみよう
配列 arr を受け取り、 reduce を使って全要素の合計を返す関数 sumAll を書きましょう。空配列のときは 0 を返してください。 reduce の初期値に 0 を渡せば自然と空配列にも対応できます。
よくある質問
Q. reduce はどんなときに使うべきですか?
A. 合計・最大値・グルーピングなど、配列を 1 つの値や別の構造にまとめたいときに有効です。単純な合計なら sum / Array.prototype.reduce((a,b)=>a+b,0) で OK ですが、複雑なロジックでは for ループの方が読みやすいこともあります。
Q. 初期値は省略しても良いですか?
A. 省略すると配列の先頭要素が初期値になり、空配列のときに TypeError になります。明示的に 0 や [] を渡しておくと安全で、型推論にも優しくなります。可読性と安全性の両面から初期値は付ける派が多数です。
Q. reduce でオブジェクトを作るには?
A. items.reduce((acc, x) => ({ ...acc, [x.id]: x }), {}) のように初期値に {} を渡し、各反復で展開構文を使います。要素数が多い場合は scatter で性能劣化するため、acc[x.id] = x; return acc; のように mutate する形も検討してください。
次のレッスン
次は 第6章まとめクイズ で、reduce で配列を 1 つの値にまとめる を学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- Array.reduce の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. Array.reduce とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
要件
- Array.prototype.reduce を使う
- 初期値に 0 を渡す
- 全要素の合計を return する
入出力例
test-cases.txt
sumAll([1,2,3,4]) → 10
sumAll([]) → 0
sumAll([10,20,30]) → 60
sumAll([-1,1,-2,2]) → 0