Movatterモバイル変換


[0]ホーム

URL:


MDN Web Docs

このページはコミュニティーの尽力で英語から翻訳されました。MDN Web Docs コミュニティーについてもっと知り、仲間になるにはこちらから。

Array.prototype.reduce()

BaselineWidely available

reduce()Array インターフェイスのメソッドで、配列のそれぞれの要素に対して、ユーザーが提供した「縮小」コールバック関数を呼び出します。その際、直前の要素の計算結果の返値を渡します。配列のすべての要素に対して「縮小」コールバック関数を実行した最終結果は、単一の値となります。

コールバックの初回実行時には「直前の計算の返値」は存在しません。初期値が与えらえた場合は、代わりに使用されることがあります。そうでない場合は、配列の要素 0 が初期値として使用され、次の要素(0 の位置ではなく 1 の位置)から反復処理が開始されます。

reduce() で一番わかりやすいのは、配列のすべての要素の和を返す場合でしょう。

試してみましょう

const array1 = [1, 2, 3, 4];// 0 + 1 + 2 + 3 + 4const initialValue = 0;const sumWithInitial = array1.reduce(  (accumulator, currentValue) => accumulator + currentValue,  initialValue,);console.log(sumWithInitial);// Expected output: 10

縮小関数は配列を要素ごとに走査し、それぞれの段階で、前の段階の結果に現在の配列の値を加えていきます (この結果は、それ以前のすべての段階を合算したものです)。

構文

js
reduce(callbackFn)reduce(callbackFn, initialValue)

引数

callbackFn

配列の各要素に対して実行される関数です。その返値は、次にcallbackFn を呼び出す際のaccumulator 引数の値になります。最後の呼び出しでは、返値はreduce() の返値となります。この関数は以下の引数で呼び出されます。

accumulator

前回のcallbackFn の呼び出し結果の値です。初回の呼び出しではinitialValue が指定されていた場合はその値、そうでない場合はarray[0] の値です。

currentValue

現在の要素の値です。初回の呼び出しではinitialValue が指定された場合はarray[0] の値であり、そうでない場合はarray[1] の値です。

currentIndex

currentValue の位置です。初回の呼び出しでは、initialValue が指定された場合は0、そうでない場合は1 です。

array

reduce() が呼び出された配列です。

initialValue省略可

コールバックが最初に呼び出された時にaccumulator が初期化される値です。initialValue が指定された場合、callbackFn は配列の最初の値をcurrentValue として実行を開始します。もしinitialValue が指定されなかった場合、accumulator は配列の最初の値に初期化され、callbackFn は配列の 2 つ目の値をcurrentValue として実行を開始します。この場合、配列が空であれば(accumulatorとして返す最初の値がなければ)エラーが発生します。

返値

配列全体にわたって「縮小」コールバック関数を実行した結果の値です。

例外

TypeError

配列に要素がなく、かつinitialValue が提供されなかった場合に発生します。

解説

reduce() メソッドは反復処理メソッドです。「縮小」コールバック関数を配列に含まれる各要素に対して昇順に一度ずつ呼び出し、その結果を単一の値に積算します。毎回、callbackFn の返値は次回のcallbackFn の呼び出しでaccumulator として渡されます。最終的なaccumulator の値(配列の最終反復処理においてcallbackFn から返される値)がreduce() の返値となります。

callbackFn は値が割り当てられている配列インデックスに対してのみ呼び出されます。疎配列の空のスロットに対しては呼び出されません。

他の反復処理メソッドとは異なり、reduce()thisArg 引数を受け入れません。callbackFn は常にundefinedthis として呼び出され、callbackFn が厳格モードでない場合はglobalThis に置き換えられます。

reduce()関数型プログラミングの中心的な概念です。ここでは、どの値も変異させることができないので、配列のすべての値を積算するには、反復処理のたびに新しい積算値を返さなければなりません。この約束事は JavaScript のreduce() にも当てはまります。スプレッド構文や他の可能な限りコピーする方法を使用して、既存のものを変更せずに、アキュームレーターとして新しい配列やオブジェクトを作成すべきなのです。もし、アキュームレーターをコピーする代わりに変化させることにした場合、コールバックで変更したオブジェクトを返すことを忘れないでください、さもなければ、次の反復処理で undefined を受け取ることになります。

reduce() メソッドは呼び出し元の配列を変更しませんが、callbackFn に指定された関数は変更することがあります。ただし、配列の長さはcallbackFn を最初に呼び出す前に保存されることに注意してください。したがって、

  • callbackFnreduce() の呼び出しを始めたときの配列の長さを超えて追加された要素にはアクセスしません。
  • 既に処理したインデックスを変更しても、callbackFn が再度呼び出されることはありません。
  • まだ処理していない既存の配列要素がcallbackFn によって変更された場合、callbackFn に渡される値はその要素が取得される時点の値になります。削除された要素はundefined であるかのように処理されます。

警告:前項で説明したような、参照中の配列の同時進行での変更は(特殊な場合を除いて)普通は避けるべきです。多くの場合、理解しにくいコードになります。

reduce() メソッドは汎用的です。これはthis 値にlength プロパティと整数キーのプロパティがあることだけを期待します。

稀な場合

配列が(位置に関わらず) 1 つの要素しか持たず、initialValue が指定されなかった場合、またはinitialValue が指定されていても配列が空だった場合、callbackFn実行されずに要素が返却されます。

initialValue が提供され、配列が空でない場合、 reduce メソッドは常に 0 の位置からコールバック関数を呼び出し始めます。

initialValue が提供されなかった場合、 reduce メソッドは、次の例に示すように、長さが 1 より大きい配列、長さが 1 の配列、長さが 0 の配列に対して異なる動作をします。

js
const getMax = (a, b) => Math.max(a, b);// コールバックは 0 の位置から配列内の全要素に対して呼び出される[1, 100].reduce(getMax, 50); // 100[50].reduce(getMax, 10); // 50// コールバックは 1 の位置に対して 1 度だけ呼び出される[1, 100].reduce(getMax); // 100// コールバックは呼び出されない[50].reduce(getMax); // 50[].reduce(getMax, 1); // 1[].reduce(getMax); // TypeError

初期値がない場合の reduce() の動作

下記のコードは、初期値がない場合に配列にreduce() を呼び出したときに何が起こるかを示します。

js
const array = [15, 16, 17, 18, 19];function reducer(accumulator, currentValue, index) {  const returns = accumulator + currentValue;  console.log(    `accumulator: ${accumulator}, currentValue: ${currentValue}, index: ${index}, returns: ${returns}`,  );  return returns;}array.reduce(reducer);

コールバック関数は 4 回呼び出され、各回の引数の内容は以下のようになります。

accumulatorcurrentValueindex返値
最初の呼び出し1516131
2 番目の呼び出し3117248
3 番目の呼び出し4818366
4 番目の呼び出し6619485

array の要素は処理中に変化しません。常に[15, 16, 17, 18, 19] です。reduce() の返値は、コールバック呼び出しの最後の返値である (85) となるでしょう。

初期値がある場合の reduce() の動作

ここでは、同じアルゴリズムで同じ配列を減らしますが、reduce() の 2 番目の引数として10 というinitialValue を渡します。

js
[15, 16, 17, 18, 19].reduce(  (accumulator, currentValue) => accumulator + currentValue,  10,);

コールバックは 5 回呼び出され、それぞれの呼び出しにおける引数と返値は次のようになります。

accumulatorcurrentValueindex返値
最初の呼び出し1015025
2 番目の呼び出し2516141
3 番目の呼び出し4117258
4 番目の呼び出し5818376
5 番目の呼び出し7619495

この場合のreduce() の返値は95 となります。

オブジェクト配列の値の合計値

オブジェクトの配列に含まれた値の合計値を出すには、すべての項目を関数内で取得できるようにするためにinitialValue を指定する必要があります

js
const objects = [{ x: 1 }, { x: 2 }, { x: 3 }];const sum = objects.reduce(  (accumulator, currentValue) => accumulator + currentValue.x,  0,);console.log(sum); // logs 6

関数を直列にパイプ接続

pipe 関数は一連の関数を受け取り、新しい関数を返します。新しい関数が引数で呼び出されると、一連の関数が順番に呼び出され、それぞれが前回関数の返値を受け取ります。

js
const pipe =  (...functions) =>  (initialValue) =>    functions.reduce((acc, fn) => fn(acc), initialValue);// 合成に使用する素材const double = (x) => 2 * x;const triple = (x) => 3 * x;const quadruple = (x) => 4 * x;// 特定の値の乗算のための合成関数const multiply6 = pipe(double, triple);const multiply9 = pipe(triple, triple);const multiply16 = pipe(quadruple, quadruple);const multiply24 = pipe(double, triple, quadruple);// 使用方法multiply6(6); // 36multiply9(9); // 81multiply16(16); // 256multiply24(10); // 240

プロミスを直列に実行

プロミスシーケンスは、非同期で行われることを除けば、基本的に前回の節で示された関数のパイプ接続です。

js
// パイプと比較すると、 fn(acc) は acc.then(fn) に変更され、// initialValue はプロミスであることが保証されます。const asyncPipe =  (...functions) =>  (initialValue) =>    functions.reduce((acc, fn) => acc.then(fn), Promise.resolve(initialValue));// 合成に使用する構成要素const p1 = async (a) => a * 5;const p2 = async (a) => a * 2;// 構成された関数は、最終的にすべてプロミスで包まれるため、// プロミス以外を返すこともできます。const f3 = (a) => a * 3;const p4 = async (a) => a * 4;asyncPipe(p1, p2, f3, p4)(10).then(console.log); // 1200

asyncPipeasync/await を使用して実装することもでき、pipe との類似性をよりよく示しています。

js
const asyncPipe =  (...functions) =>  (initialValue) =>    functions.reduce(async (acc, fn) => fn(await acc), initialValue);

reduce() を疎配列で使用

reduce() は疎配列の欠落している要素をスキップしますが、値がundefined の場合はスキップしません。

js
console.log([1, 2, , 4].reduce((a, b) => a + b)); // 7console.log([1, 2, undefined, 4].reduce((a, b) => a + b)); // NaN

配列以外のオブジェクトに対する reduce() の呼び出し

reduce() メソッドはthislength プロパティを読み込み、次にキーがlength より小さい非負の整数である各プロパティにアクセスします。

js
const arrayLike = {  length: 3,  0: 2,  1: 3,  2: 4,  3: 99, // length が 3 であるため reduce() からは無視される};console.log(Array.prototype.reduce.call(arrayLike, (x, y) => x + y));// 9

reduce() を使用すべきでない場合

reduce() のような多目的の高次関数は強力ですが、特に経験の浅い JavaScript の開発者にとっては理解しにくい場合があります。他の配列メソッドを使用した方がコードが明快になる場合、開発者はreduce() を使用する他の利点と読み取り可能性のトレードオフを比較検討する必要があります。

reduce() は常にfor...of ループと同等ですが、上位スコープの変数を変更する代わりに、各反復処理で新しい値を返すことに注意してください。

js
const val = array.reduce((acc, cur) => update(acc, cur), initialValue);// これは、次のものと同等ですlet val = initialValue;for (const cur of array) {  val = update(val, cur);}

前述のように、人々がreduce() を使用したいと思う理由は、データを不変とする関数型プログラミングの手法を模倣するためです。したがって、アキュムレーターの不変性を支持する開発者は、次のように反復処理ごとにアキュムレーター全体をコピーする傾向があります。

js
const names = ["Alice", "Bob", "Tiff", "Bruce", "Alice"];const countedNames = names.reduce((allNames, name) => {  const currCount = Object.hasOwn(allNames, name) ? allNames[name] : 0;  return {    ...allNames,    [name]: currCount + 1,  };}, {});

このコードは非効率的です。というのも、各イテレーターはallNames オブジェクト全体をコピーする必要があり、これは固有の名前がいくつあるかによってサイズが大きくなってしまうからです。このコードは最悪の場合、Nnames の長さだとするとO(N^2) のパフォーマンスになります。

各反復処理でallNames オブジェクトを変更したほうがよりよいでしょう。しかし、allNames がいずれにせよ変更されるのであれば、reduce() を単純なfor ループに変換した方がより明確です。

js
const names = ["Alice", "Bob", "Tiff", "Bruce", "Alice"];const countedNames = names.reduce((allNames, name) => {  const currCount = allNames[name] ?? 0;  allNames[name] = currCount + 1;  // allNames を返さないと、次の反復処理で undefined を受け取る  return allNames;}, Object.create(null));
js
const names = ["Alice", "Bob", "Tiff", "Bruce", "Alice"];const countedNames = Object.create(null);for (const name of names) {  const currCount = countedNames[name] ?? 0;  countedNames[name] = currCount + 1;}

したがって、アキュムレーターが配列やオブジェクトで、反復処理ごとに配列やオブジェクトをコピーしている場合、誤ってコードに 2 次的な複雑さを導入してしまい、大きなデータですぐにパフォーマンスが低下してしまう可能性があります。

reduce() の受け入れられる用途のいくつかは上で指定されたものです(特に、配列の合計、プロミスの順序付け、関数のパイプ処理)。他にもreduce() よりも優れた代替手段が存在する場合があります。

  • 配列の平坦化。代わりにflat() を使用してください。

    js
    const flattened = array.reduce((acc, cur) => acc.concat(cur), []);
    js
    const flattened = array.flat();
  • プロパティによるオブジェクトのグループ化。代わりにObject.groupBy() を使用してください。

    js
    const groups = array.reduce((acc, obj) => {  const key = obj.name;  const curGroup = acc[key] ?? [];  return { ...acc, [key]: [...curGroup, obj] };}, {});
    js
    const groups = Object.groupBy(array, (obj) => obj.name);
  • オブジェクトの配列に格納されている配列の連結。代わりにflatMap() を使用してください。

    js
    const friends = [  { name: "Anna", books: ["Bible", "Harry Potter"] },  { name: "Bob", books: ["War and peace", "Romeo and Juliet"] },  { name: "Alice", books: ["The Lord of the Rings", "The Shining"] },];const allBooks = friends.reduce((acc, cur) => [...acc, ...cur.books], []);
    js
    const allBooks = friends.flatMap((person) => person.books);
  • 配列内の重複項目の除去。代わりにSetArray.from() を使用してください。

    js
    const uniqArray = array.reduce(  (acc, cur) => (acc.includes(cur) ? acc : [...acc, cur]),  [],);
    js
    const uniqArray = Array.from(new Set(array));
  • 配列の要素の削除や追加。代わりにflatMap() を使用してください。

    js
    // 数値の配列を受け取り、完全二乗を平方根に分割します。const roots = array.reduce((acc, cur) => {  if (cur < 0) return acc;  const root = Math.sqrt(cur);  if (Number.isInteger(root)) return [...acc, root, root];  return [...acc, cur];}, []);
    js
    const roots = array.flatMap((val) => {  if (val < 0) return [];  const root = Math.sqrt(val);  if (Number.isInteger(root)) return [root, root];  return [val];});

    配列から要素を取り除くだけならfilter() も使用できます。

  • 要素の検索、または要素が条件を満たすかどうかのテスト。代わりにfind()findIndex() またはsome()every() を使用してください。これらのメソッドには、配列全体を反復処理することなく、結果が確定したらすぐに返すという好ましいこともあります。

    js
    const allEven = array.reduce((acc, cur) => acc && cur % 2 === 0, true);
    js
    const allEven = array.every((val) => val % 2 === 0);

reduce() が最良の選択である場合は、ドキュメント化と変数名の意味づけをすることで、可読性の欠点を軽減する手助けになります。

仕様書

Specification
ECMAScript® 2026 Language Specification
# sec-array.prototype.reduce

ブラウザーの互換性

関連情報

Help improve MDN

Learn how to contribute.

This page was last modified on byMDN contributors.


[8]ページ先頭

©2009-2025 Movatter.jp