JavaScriptのreduce()とは?配列を1つにまとめる方法

JavaScriptのreduce関数によって複数の要素が1つの値に集約されるイメージ
配列の要素を1つの値へまとめるJavaScriptのreduce()の仕組みを視覚的に表現したアイキャッチ画像

JavaScriptのreduce()は、配列の要素を順番に処理しながら、最終的にひとつの値へまとめるメソッドです。

合計値を求める処理で紹介されることが多いですが、reduce()でできることはそれだけではありません。数値の集計、文字列の連結、オブジェクトへの変換、要素の分類など、さまざまな場面で活用できます。

一方で、map()filter()よりも少し考え方が難しく、はじめて学ぶ段階では「何を返せばいいのか分からない」「初期値は必要なのか迷う」と感じやすいメソッドでもあります。

この記事では、JavaScriptのreduce()の基本から、書き方、初期値の考え方、よくあるミス、実務でも使いやすい例まで、順番に丁寧に解説します。

Contents

reduce()とは何か

配列を順番に処理して1つの結果にまとめるメソッドです

reduce()は、配列の先頭から順に要素を取り出し、それまでの計算結果と現在の要素を組み合わせながら、最終的にひとつの値を作るメソッドです。

たとえば、次のような処理に向いています。

  • 配列の数値を合計する
  • 配列の文字列を連結する
  • 配列からオブジェクトを作る
  • 条件ごとにデータを分類する
  • 要素の出現回数を数える

「複数の要素を、何らかのルールで1つにまとめる」ときに使うのがreduce()です。

reduceという名前の意味

reduceには「減らす」「まとめる」といった意味があります。
JavaScriptのreduce()でも、配列の複数の要素を処理しながら、最終的に1つの結果へ集約していきます。

ただし、必ずしも数を減らすとは限りません。
最終結果が数値とは限らず、文字列、配列、オブジェクトになることもあります。

reduce()の基本構文

基本の書き方

// reduce-basic.js
const result = array.reduce(function (accumulator, currentValue) {
  return 更新後の値;
}, initialValue);

console.log(result);

アロー関数で書くと、次のようになります。

// reduce-basic-arrow.js
const result = array.reduce((accumulator, currentValue) => {
  return 更新後の値;
}, initialValue);

console.log(result);

引数の意味

reduce()のコールバック関数では、主に次の引数を使います。

  • accumulator
    これまでの処理結果を保持する値です。累積値、集計値などと呼ばれることがあります。
  • currentValue
    現在処理している配列の要素です。

必要に応じて、さらに次の引数も使えます。

  • currentIndex
    現在のインデックスです。
  • array
    対象の配列そのものです。

書式としては次のようになります。

// reduce-args.js
const result = array.reduce(function (accumulator, currentValue, currentIndex, array) {
  return 更新後の値;
}, initialValue);

console.log(result);

いちばん重要なのはreturnです

reduce()では、コールバック関数のreturnで返した値が、次の回のaccumulatorになります。

そのため、returnを書き忘れると、意図した結果になりません。

この例では、1回目のreturn結果が次のsumになり、最終的に合計値の6になります。

reduce()で合計を求める基本例

数値の合計

もっとも基本的な使い方は、配列の数値を合計する処理です。

// reduce-sum.js
const numbers = [10, 20, 30, 40];

const total = numbers.reduce(function (sum, number) {
  return sum + number;
}, 0);

console.log(total); // 100

このコードでは、0を初期値として、各要素を順番に足しています。

処理の流れは次のとおりです。

  • 初回: 0 + 1010
  • 2回目: 10 + 2030
  • 3回目: 30 + 3060
  • 4回目: 60 + 40100

アロー関数で簡潔に書く例

// reduce-sum-arrow.js
const numbers = [10, 20, 30, 40];

const total = numbers.reduce((sum, number) => sum + number, 0);

console.log(total); // 100

短く書けますが、reduce()に慣れないうちは、波かっこ付きでreturnを明示する形のほうが読みやすいこともあります。

初期値とは何か

初期値はaccumulatorの最初の値です

reduce()の第2引数に渡す値が初期値です。
この初期値が、最初のaccumulatorになります。

// reduce-initial-value.js
const numbers = [1, 2, 3];

const result = numbers.reduce(function (sum, number) {
  return sum + number;
}, 100);

console.log(result); // 106

この例では、初期値が100なので、計算は100 + 1 + 2 + 3になります。

初期値を省略した場合

初期値を省略すると、配列の先頭要素が最初のaccumulatorとして使われ、2番目の要素から処理が始まります。

// reduce-no-initial.js
const numbers = [10, 20, 30];

const total = numbers.reduce(function (sum, number) {
  return sum + number;
});

console.log(total); // 60

この場合の流れは次のとおりです。

  • 最初のsum10
  • 最初のnumber20
  • その後、30を処理する

初期値は原則として指定するのが安全です

初学者向けにも実務向けにも、reduce()では初期値を明示する書き方が基本です。理由は次の2つです。

  • 処理の開始条件が分かりやすい
  • 空配列でエラーになるのを避けやすい

たとえば空配列で初期値を省略すると、エラーになります。

// reduce-empty-error.js
const numbers = [];

const total = numbers.reduce(function (sum, number) {
  return sum + number;
});

console.log(total);

このコードは実行時にエラーになります。

一方、初期値があれば空配列でも安全に処理できます。

// reduce-empty-safe.js
const numbers = [];

const total = numbers.reduce(function (sum, number) {
  return sum + number;
}, 0);

console.log(total); // 0

reduce()の処理の流れを図のように理解する

accumulatorが次へ渡されていきます

reduce()を理解するうえで大切なのは、「返した値が次に渡る」という流れです。

// reduce-flow.js
const numbers = [1, 2, 3, 4];

const result = numbers.reduce(function (accumulator, currentValue) {
  console.log("accumulator:", accumulator, "currentValue:", currentValue);
  return accumulator + currentValue;
}, 0);

console.log("result:", result);

実行すると、概ね次のような流れになります。

  • accumulator: 0, currentValue: 1
  • accumulator: 1, currentValue: 2
  • accumulator: 3, currentValue: 3
  • accumulator: 6, currentValue: 4

最後に10が返ります。

この仕組みが理解できると、合計以外の応用もしやすくなります。

reduce()でできること

文字列を連結する

配列の文字列をつなげて1つの文章にすることもできます。

// reduce-string-join.js
const words = ["JavaScript", "の", "reduce()", "を", "学ぶ"];

const sentence = words.reduce(function (text, word) {
  return text + word;
}, "");

console.log(sentence); // JavaScriptのreduce()を学ぶ

配列からオブジェクトを作る

配列の値をもとに、別の形のデータへ変換することもできます。

// reduce-array-to-object.js
const fruits = ["apple", "banana", "orange"];

const result = fruits.reduce(function (object, fruit) {
  object[fruit] = fruit.length;
  return object;
}, {});

console.log(result);
// { apple: 5, banana: 6, orange: 6 }

要素の出現回数を数える

同じ値が何回出てきたかを数える集計にも向いています。

// reduce-count.js
const fruits = ["apple", "banana", "apple", "orange", "banana", "apple"];

const counts = fruits.reduce(function (result, fruit) {
  if (result[fruit] === undefined) {
    result[fruit] = 1;
  } else {
    result[fruit] += 1;
  }

  return result;
}, {});

console.log(counts);
// { apple: 3, banana: 2, orange: 1 }

条件ごとに分類する

たとえば、数値を偶数と奇数に分けることもできます。

// reduce-group.js
const numbers = [1, 2, 3, 4, 5, 6];

const grouped = numbers.reduce(function (result, number) {
  if (number % 2 === 0) {
    result.even.push(number);
  } else {
    result.odd.push(number);
  }

  return result;
}, { even: [], odd: [] });

console.log(grouped);
// { even: [2, 4, 6], odd: [1, 3, 5] }

reduce()とmap()やfilter()の違い

map()は各要素を変換して新しい配列を作ります

map()は、配列の各要素を変換し、同じ長さの新しい配列を返すメソッドです。

// map-example.js
const numbers = [1, 2, 3];

const doubled = numbers.map(function (number) {
  return number * 2;
});

console.log(doubled); // [2, 4, 6]

map()については、基礎から整理したい場合にJavaScriptのmap()についての解説記事もあわせて確認すると、役割の違いが分かりやすくなります。

filter()は条件に合う要素だけを取り出します

filter()は、条件に合う要素だけを集めた新しい配列を返します。

// filter-example.js
const numbers = [1, 2, 3, 4, 5];

const evenNumbers = numbers.filter(function (number) {
  return number % 2 === 0;
});

console.log(evenNumbers); // [2, 4]

絞り込み処理の基本は、JavaScriptのfilter()とは?配列の絞り込みを徹底解説であわせて確認できます。

reduce()は最終結果を自由に作れます

reduce()は、戻り値の形が固定されていません。
数値にも文字列にも配列にもオブジェクトにもできる点が特徴です。

そのぶん柔軟ですが、処理内容が見えにくくなりやすいため、単純な変換ならmap()、単純な絞り込みならfilter()を使うほうが読みやすい場面もあります。

無理にreduce()へ統一しないことも大切です

reduce()は便利ですが、何でもreduce()で書く必要はありません。

  • 要素を変換するだけならmap()
  • 条件で絞るだけならfilter()
  • 文字列をつなげるだけならjoin()

というように、目的に合ったメソッドを選ぶことが読みやすいコードにつながります。

配列メソッド全体の使い分けは、JavaScriptの配列操作まとめ(map・filter・reduce)も参考になります。
また、文字列の連結ではJavaScriptのjoin()についての解説記事も関連知識として役立ちます。

reduce()のよくある間違い

returnを書き忘れる

もっとも多いミスが、コールバック関数内でreturnを書き忘れることです。

// reduce-no-return.js
const numbers = [1, 2, 3];

const result = numbers.reduce(function (sum, number) {
  sum + number;
}, 0);

console.log(result); // 意図した結果にならない

このコードではsum + numberを計算していても、その値を返していないため、次のsumに正しく渡りません。

修正例はこちらです。

// reduce-with-return.js
const numbers = [1, 2, 3];

const result = numbers.reduce(function (sum, number) {
  return sum + number;
}, 0);

console.log(result); // 6

初期値を省略して空配列でエラーになる

先ほど触れたとおり、空配列に対して初期値なしでreduce()を使うとエラーになります。

// reduce-empty-array.js
const numbers = [];

const result = numbers.reduce(function (sum, number) {
  return sum + number;
});

console.log(result);

安全性と可読性のためにも、初期値は明示するのが基本です。

accumulatorの中身を理解せずに書いてしまう

accumulatorに何を入れていくのかを曖昧にしたまま書くと、処理が分かりにくくなります。

たとえば合計なら「数値」、分類なら「オブジェクト」、抽出なら「配列」のように、最終的に何を作りたいかを先に決めることが大切です。

実務でも使いやすいreduce()の例

商品金額の合計を計算する

オブジェクト配列から特定の値だけを集計する例です。

// reduce-total-price.js
const items = [
  { name: "ノート", price: 120 },
  { name: "ペン", price: 80 },
  { name: "消しゴム", price: 100 }
];

const totalPrice = items.reduce(function (sum, item) {
  return sum + item.price;
}, 0);

console.log(totalPrice); // 300

条件に合うデータだけの合計を出す

購入済みの商品の合計だけを求めることもできます。

// reduce-conditional-total.js
const items = [
  { name: "ノート", price: 120, purchased: true },
  { name: "ペン", price: 80, purchased: false },
  { name: "消しゴム", price: 100, purchased: true }
];

const totalPrice = items.reduce(function (sum, item) {
  if (item.purchased) {
    return sum + item.price;
  }

  return sum;
}, 0);

console.log(totalPrice); // 220

このような処理はfilter()してからreduce()する書き方もできます。
可読性を優先するなら、処理を分けるほうが分かりやすい場面もあります。

reduce()はいつ使うべきか

複数の要素から1つの結果を作るときです

reduce()が向いているのは、配列をもとに最終結果をひとつ作りたい場面です。

たとえば次のようなケースです。

  • 合計値や平均値を求める
  • 集計表を作る
  • 配列をオブジェクトに変換する
  • 条件ごとに分類する
  • 複数の値をつなげて1つにする

読みにくくなる場合は別の方法も検討します

reduce()は便利ですが、複雑な処理を1つに詰め込みすぎると、かえって読みづらくなります。

コードの分かりやすさを優先するなら、次のように分けて考えるのも有効です。

  • まずfilter()で絞る
  • 次にmap()で必要な値へ変換する
  • 最後にreduce()で集計する

こうした配列メソッドの連携を理解するうえでも、JavaScriptのfind()とは?最初の要素を取得する方法JavaScriptのincludes()についての解説記事など、周辺メソッドもあわせて学ぶと理解が深まります。

reduce()を理解するコツ

先に最終形を決めると考えやすくなります

reduce()では、最終的に何を作りたいかを最初に決めると理解しやすくなります。

たとえば次のように考えます。

  • 合計なら最終形は数値
  • 連結なら最終形は文字列
  • 分類なら最終形はオブジェクト
  • 抽出なら最終形は配列

この最終形に合わせて、初期値も決まります。

  • 数値なら0
  • 文字列なら""
  • 配列なら[]
  • オブジェクトなら{}

1回ごとの変化を追うと理解しやすくなります

reduce()が難しく感じる場合は、いきなり短く書かず、console.log()accumulatorcurrentValueの変化を見るのがおすすめです。

処理の流れが見えると、何を返せばよいかが分かりやすくなります。

まとめ

JavaScriptのreduce()は、配列の要素を順番に処理しながら、最終的に1つの値へまとめるメソッドです。

基本の考え方は次のとおりです。

  • accumulatorにこれまでの結果が入る
  • currentValueに現在の要素が入る
  • returnした値が次のaccumulatorになる
  • 初期値は明示するほうが安全で分かりやすい

reduce()は合計計算だけでなく、文字列連結、件数集計、分類、オブジェクト変換などにも使えます。
一方で、用途によってはmap()filter()のほうが適している場面もあるため、目的に応じて使い分けることが大切です。

まずは、数値の合計のような基本例から慣れ、次にオブジェクト変換やグループ分けへ進むと、理解しやすくなります。

関連記事