Array.prototype.reduce()

reduce() は配列を1つの値にまとめます。初期値を省いたときの落とし穴、空配列で例外になる条件、合計・グループ化の書き方、いつ使わないほうがよいかまでまとめます。

実行して検証済み処理系Node 22.22.3対応ES5難易度●●●○○目安10更新

reduce() は、配列を1つの値にまとめます。 累積値を持ち回りながら、要素を1つずつ畳み込んでいきます。

example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
10
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
0回目: 累積 0 + 要素 1 = 1 1回目: 累積 1 + 要素 2 = 3 2回目: 累積 3 + 要素 3 = 6 結果: 6

第2引数の 0初期値です。省略できますが、省略しないでください(理由は後述)。

上のコードは書き換えて実行できます。sum + nsum * n01 にしてみてください。

引数

中身
第1引数 コールバック関数 (累積値, 要素, 添字, 配列) => 次の累積値
第2引数 初期値(省略可。省略しないほうがよい
戻り値 最後の累積値。配列とは限らない
元の配列 変わらない

何が起きているか

累積値がどう渡っていくかを見ます。

javascript
const nums = [1, 2, 3];

const total = nums.reduce((sum, n) => {
sum + n;
}, 0);

console.log(total);
undefined
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
6 6

返した値が、次の回の第1引数になります。 ここが map() との一番の違いです。

return を書き忘れると、次の回の累積値が undefined になります。

javascript
const items = [
{ price: 100 },
{ price: 200 },
];

console.log(items.reduce((sum, item) => sum + item.price));
[object Object]200
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
300

初期値を省くと危ない

初期値を省くと、先頭の要素がそのまま最初の累積値になり、2番目から回り始めます。

javascript
console.log([].reduce((a, b) => a + b));
TypeError: Reduce of empty array with no initial value
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
0 []

数の合計なら同じ結果です。ですが型が違うと壊れます。

example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
316 95 95
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
{ 'りんご': 3, 'みかん': 1, 'ぶどう': 1 }

最初の累積値がオブジェクトそのものになり、文字列として繋がりました。 初期値 0 を書けば直ります。

example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
{ A: [ 'あかり', 'はると' ], B: [ 'ゆうと' ] }
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
[ 'あかり', 'はると' ]

空配列では例外になる

初期値が無く、要素も無いと、返すものが決まりません。 仕様で TypeError と定められています。

javascript
const nums = [1, 2, 3, 4];

const doubled = nums.reduce((acc, n) => {
acc.push(n * 2);
return acc;
}, []);

console.log(doubled);
[ 2, 4, 6, 8 ]
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
[ 2, 4, 6, 8 ]

初期値があれば、そのまま返ります。

javascript
const votes = ['a', 'b', 'a'];

const count = votes.reduce((acc, v) => ({ ...acc, [v]: (acc[v] ?? 0) + 1 }), {});

console.log(count);
{ a: 2, b: 1 }
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
4 呼ばれた回数: 2

空配列は普通に起こりますfilter() の結果など)。 初期値は必ず書いてください

よく使う形

合計・最大

example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
abc cba

最大値は Math.max(...arr) のほうが短く読めます。ただし要素が数万を超えると RangeError になるので、そのときは reduce にしてください。

合計まわりの落とし穴(小数・空配列・文字列が混ざる)は 配列の合計を出す にまとめてあります。

数える

初期値が {} なので、戻り値は配列ではなくオブジェクトです。

グループ化

Object.groupBy() を使うともっと短く書けます(ES2024。Node 21 / Chrome 117 以降)。

使わないほうがよいとき

reduce() は何でも書けてしまいます。書けることと、読みやすいことは別です。

これは map() そのものです。短く、意図もはっきりします。

やりたいこと 使うもの
全部を変換する map()
条件で絞る filter()
最初の1件を探す find()
1つの値にまとめる reduce()

reduce() を選ぶのは、出てくる形が元と違うときだけにしてください。 配列が配列のまま出てくるなら、たいてい別のメソッドが合っています。

累積値を毎回作り直さない

短く書きたくてこう書くと、要素の数だけオブジェクトを作り直します。

結果は合っています。ですが { ...acc }毎回まるごと複製しています。 要素が増えると急に遅くなります。書き換えて返してください(上の「数える」の形)。

穴のある配列では呼ばれない

疎な配列の穴では、コールバック関数は呼ばれません。

map() と同じ扱いです。find() は穴でも呼ばれるので、そこだけ違います。

右から畳み込む

reduceRight() は末尾から回ります。順序が結果に影響するときだけ使います。

この記事の根拠

  1. Array.prototype.reduce — ECMAScript® 2026 Language Specificationtc39.es
  2. Array.prototype.reduce() — MDNdeveloper.mozilla.org

掲載しているコード例は、公開前に読み手が押したときに動くのと同じもので実行して出力を突き合わせています。結果はリポジトリに残しています。

このページの原文(Markdown)