Array.reduce

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

Array.reduce とは

reduce で配列を 1 つの値にまとめる。本レッスンでは、Array.reduce の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。

reduce で 1 つの値にまとめる

map は加工、 filter は絞り込み、そして reduce集約 を担当します。 Array.prototype.reduce を使うと、配列を 合計最大値平均オブジェクトへの集約 などに変換できます。配列メソッドの中で最も汎用的で、最初は少し戸惑うけれど、慣れると何にでも使える万能ツールです。

reduce は配列を 1 つの値 にまとめるためのメソッドです。 合計 だけでなく、 配列をオブジェクトに変換グループ化 などにも使えます。

mapfilter は新しい配列を返しますが、 reduce は数値・文字列・オブジェクトなど 何でも 返せます。これが reduce を強力にしている理由です。

reduce の文法

書き方は次のとおりです。

JavaScript

const nums = [1, 2, 3, 4]; const total = nums.reduce((acc, n) => acc + n, 0); console.log(total); // 10

reduce の引数は 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 つだけで十分です。

動きを追ってみる

合計を求める処理は内部的にこんな感じで進みます。

diagram (will load when visible)

ひとつずつ要素を取り出して 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 単体で使うこともありますが、 mapfilter と組み合わせて 処理のパイプライン を作ると 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 を書き忘れて、 accundefined になっていく
  • 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 つができることを確認しましょう。

  1. Array.reduce の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. Array.reduce とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

要件

  1. Array.prototype.reduce を使う
  2. 初期値に 0 を渡す
  3. 全要素の合計を return する

入出力例

test-cases.txt

sumAll([1,2,3,4])10 sumAll([])0 sumAll([10,20,30])60 sumAll([-1,1,-2,2])0

ヒント

main.js
main.js
学習モード

メモ

Array.reduce

⌘S で保存