--- title: Array.prototype.reduce() slug: Web/JavaScript/Reference/Global_Objects/Array/reduce tags: - Array - 配列メソッド - ECMAScript 5 - JavaScript - メソッド - プロトタイプ - Reduce - リファレンス - ポリフィル browser-compat: javascript.builtins.Array.reduce translation_of: Web/JavaScript/Reference/Global_Objects/Array/reduce --- {{JSRef}} **`reduce()`** メソッドは、配列のそれぞれの要素に対してユーザーが提供した「縮小」コールバック関数を呼び出します。その際、直前の要素における計算結果の返値を渡します。配列のすべての要素に対して縮小関数を実行した結果が単一の値が最終結果になります。 `reduce()` で一番わかりやすいのは、配列のすべての要素の和を返す場合でしょう。 縮小関数は配列を要素ごとに走査し、それぞれの段階で、前の段階の結果に現在の配列の値を加えていきます (この結果は、それ以前のすべての段階を合算したものです)。 次のインタラクティブサンプルで紹介します。 {{EmbedInteractiveExample("pages/js/array-reduce.html")}} ## 構文 ```js // アロー関数 reduce((previousValue, currentValue) => { ... } ) reduce((previousValue, currentValue, currentIndex) => { ... } ) reduce((previousValue, currentValue, currentIndex, array) => { ... } ) reduce((previousValue, currentValue, currentIndex, array) => { ... }, initialValue) // コールバック関数 reduce(callbackFn) reduce(callbackFn, initialValue) // インラインコールバック関数 reduce(function callbackFn(previousValue, currentValue) { ... }) reduce(function callbackFn(previousValue, currentValue, currentIndex) { ... }) reduce(function callbackFn(previousValue, currentValue, currentIndex, array){ ... }) reduce(function callbackFn(previousValue, currentValue, currentIndex, array) { ... }, initialValue) ``` ### 引数 - `callbackFn` - : 4 つの引数を取る「縮小」関数です。 - *previousValue* (前回の `callbackfn` の呼び出し結果の値) - *currentValue* (現在の要素の値) - *currentIndex* (現在の位置) {{optional_inline}} - *array* (走査する配列) {{optional_inline}} - `initialValue` {{optional_inline}} - : コールバックが初めて呼び出されたときの *previousValue* の初期値です。 `initialValue` が指定された場合は、 *currentValue* も配列の最初の値に初期化されます。 `initialValue` が指定され*なかった*場合、 *previousValue* は配列の最初の値で初期化され、 *currentValue* は配列の 2 番目の値で初期化されます。 ### 返値 配列全体にわたって「縮小」コールバック関数を実行した結果の値です。 ### 例外 {{jsxref("TypeError")}}: 配列に要素がなく、かつ `initialValue` が提供されなかった場合に発生します。 ## 解説 ECMAScript の仕様書は、 `reduce()` の動作を次のように記述しています。 > *callbackfn* は、4 つの引数を取る関数でなければなりません。 `reduce` は、配列の最初の要素の後の各要素に対して、昇順にコールバックを関数として呼び出します。 > > *callbackfn* は次の 4 つの引数で呼び出されます。 > > - the *previousValue* (前回の *callbackfn* の呼び出し結果の値) > - the *currentValue* (現在の要素の値) > - the *currentIndex* > - 走査中のオブジェクト > そのコールバックが最初に呼び出されるとき、 *previousValue* と *currentValue* は以下の 2 通りのうちのどちらかになります。 > - *initialValue* が `reduce` の呼び出しによって与えられた場合、 *previousValue* は to *initialValue* と同じになり、 *currentValue* は配列の最初の値と等しくなります。 > - *initialValue* が与えられていない場合は、 *previousValue* は配列の最初の値と同じになり、 *currentValue* は 2 番目の値と同じになります。 > 配列に要素がなく、かつ *initialValue* が与えられていない場合は {{jsxref("TypeError")}} が発生します。 > > `reduce` は、呼び出されたオブジェクトを直接は変更しませんが、 *callbackfn* の呼び出しによってオブジェクトが変更される可能性はあります。 > > `reduce` で処理される要素の範囲は、 *callbackfn* が最初に呼び出される前に設定されます。 `reduce` の呼び出しが始まった後に配列に追加された要素は、 *callbackfn* が処理することはありません。配列の既存の要素が変更された場合、 *callbackfn* には `reduce` がその値を処理する時点の値が渡されます。 `reduce` が呼び出された後、処理されるまでに削除された要素は処理されません。 配列が (位置に関わらず) 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 [0, 1, 2, 3, 4].reduce(function(previousValue, currentValue, currentIndex, array) { return previousValue + currentValue }) ``` コールバック関数は 4 回呼び出され、各回の引数の内容は以下のようになります。
callback の反復処理 previousValue currentValue currentIndex array 返値
初回の呼出し 0 1 1 [0, 1, 2, 3, 4] 1
2 回目の呼出し 1 2 2 [0, 1, 2, 3, 4] 3
3 回目の呼出し 3 3 3 [0, 1, 2, 3, 4] 6
4 回目の呼出し 6 4 4 [0, 1, 2, 3, 4] 10
`reduce()` の返値は、コールバック呼び出しの最後の返値である (`10`) となるでしょう。 通常の関数の代わりに{{jsxref("Functions/Arrow_functions", "アロー関数","",1)}}を指定することができます。下記のコードは上記のコードと同じ結果を返します。 ```js [0, 1, 2, 3, 4].reduce( (previousValue, currentValue, currentIndex, array) => previousValue + currentValue ) ``` *initialValue* を `reduce()` の 2 つ目の引数に渡した場合は、結果は次のようになります。 ```js [0, 1, 2, 3, 4].reduce((previousValue, currentValue, currentIndex, array) => { return previousValue + currentValue }, 10) ```
callback の反復処理 previousValue currentValue currentIndex array 返値
初回の呼出し 10 0 0 [0, 1, 2, 3, 4] 10
2 回目の呼出し 10 1 1 [0, 1, 2, 3, 4] 11
3 回目の呼出し 11 2 2 [0, 1, 2, 3, 4] 13
4 回目の呼出し 13 3 3 [0, 1, 2, 3, 4] 16
5 回目の呼出し 16 4 4 [0, 1, 2, 3, 4] 20
この場合の `reduce()` の返値は `20` となります。

## 例 ### 配列内の値の合計値を出す ```js let sum = [0, 1, 2, 3].reduce(function (previousValue, currentValue) { return previousValue + currentValue }, 0) // sum is 6 ``` また、アロー関数を用いて書くこともできます。 ```js let total = [ 0, 1, 2, 3 ].reduce( ( previousValue, currentValue ) => previousValue + currentValue, 0 ) ``` ### オブジェクトの配列の値の合計値を出す オブジェクトの配列に含まれた値の合計値を出すには、すべての項目を関数内で取得できるようにするために *initialValue* を指定する**必要があります**。 ```js let initialValue = 0 let sum = [{x: 1}, {x: 2}, {x: 3}].reduce(function (previousValue, currentValue) { return previousValue + currentValue.x }, initialValue) console.log(sum) // logs 6 ``` また、アロー関数を用いて書くこともできます。 ```js let initialValue = 0 let sum = [{x: 1}, {x: 2}, {x: 3}].reduce( (previousValue, currentValue) => previousValue + currentValue.x , initialValue ) console.log(sum) // logs 6 ``` ### 二次元配列を一次元配列にする ```js let flattened = [[0, 1], [2, 3], [4, 5]].reduce( function(previousValue, currentValue) { return previousValue.concat(currentValue) }, [] ) // flattened is [0, 1, 2, 3, 4, 5] ``` また、アロー関数を用いて書くこともできます。 ```js let flattened = [[0, 1], [2, 3], [4, 5]].reduce( ( previousValue, currentValue ) => previousValue.concat(currentValue), [] ) ``` ### オブジェクトの値のインスタンスを数える ```js let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'] let countedNames = names.reduce(function (allNames, name) { if (name in allNames) { allNames[name]++ } else { allNames[name] = 1 } return allNames }, {}) // countedNames is: // { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 } ``` ### プロパティによってオブジェクトをグループ化 ```js let people = [ { name: 'Alice', age: 21 }, { name: 'Max', age: 20 }, { name: 'Jane', age: 20 } ]; function groupBy(objectArray, property) { return objectArray.reduce(function (acc, obj) { let key = obj[property] if (!acc[key]) { acc[key] = [] } acc[key].push(obj) return acc }, {}) } let groupedPeople = groupBy(people, 'age') // groupedPeople is: // { // 20: [ // { name: 'Max', age: 20 }, // { name: 'Jane', age: 20 } // ], // 21: [{ name: 'Alice', age: 21 }] // } ``` ### スプレッド演算子と initialValue を使ってオブジェクトの配列に含まれる配列を結合させる ```js // friends - an array of objects // where object field "books" is a list of favorite books let friends = [{ name: 'Anna', books: ['Bible', 'Harry Potter'], age: 21 }, { name: 'Bob', books: ['War and peace', 'Romeo and Juliet'], age: 26 }, { name: 'Alice', books: ['The Lord of the Rings', 'The Shining'], age: 18 }] // allbooks - list which will contain all friends' books + // additional list contained in initialValue let allbooks = friends.reduce(function(previousValue, currentValue) { return [...previousValue, ...currentValue.books] }, ['Alphabet']) // allbooks = [ // 'Alphabet', 'Bible', 'Harry Potter', 'War and peace', // 'Romeo and Juliet', 'The Lord of the Rings', // 'The Shining' // ] ``` ### 配列内の重複要素を除去する > **Note:** {{jsxref("Set")}} と {{jsxref("Array.from()")}} に対応している環境を使っている場合は、`let orderedArray = Array.from(new Set(myArray))` を使うことで重複要素を除去された配列を取得することができます。 ```js let myArray = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd'] let myArrayWithNoDuplicates = myArray.reduce(function (previousValue, currentValue) { if (previousValue.indexOf(currentValue) === -1) { previousValue.push(currentValue) } return previousValue }, []) console.log(myArrayWithNoDuplicates) ``` ### .filter().map() を .reduce() で置き換える {{jsxref("Array.filter()")}} を使用した後で {{jsxref("Array.map()")}} を使用すると配列を二度走査しますが、{{jsxref("Array.reduce()")}} では同じ効果を一度の操作で実現することができ、もっと効率的です。(for ループが好きなのであれば、{{jsxref("Array.forEach()")}} で一度の操作で filter と map を行うことができます)。 ```js const numbers = [-5, 6, 2, 0,]; const doubledPositiveNumbers = numbers.reduce((previousValue, currentValue) => { if (currentValue > 0) { const doubled = currentValue * 2; previousValue.push(doubled); } return previousValue; }, []); console.log(doubledPositiveNumbers); // [12, 4] ``` ### シーケンス上の Promise を動かす ```js /** * Runs promises from array of functions that can return promises * in chained manner * * @param {array} arr - promise arr * @return {Object} promise object */ function runPromiseInSequence(arr, input) { return arr.reduce( (promiseChain, currentFunction) => promiseChain.then(currentFunction), Promise.resolve(input) ) } // promise function 1 function p1(a) { return new Promise((resolve, reject) => { resolve(a * 5) }) } // promise function 2 function p2(a) { return new Promise((resolve, reject) => { resolve(a * 2) }) } // function 3 - will be wrapped in a resolved promise by .then() function f3(a) { return a * 3 } // promise function 4 function p4(a) { return new Promise((resolve, reject) => { resolve(a * 4) }) } const promiseArr = [p1, p2, f3, p4] runPromiseInSequence(promiseArr, 10) .then(console.log) // 1200 ``` ### パイプによって関数を合成する ```js // Building-blocks to use for composition const double = x => x + x const triple = x => 3 * x const quadruple = x => 4 * x // Function composition enabling pipe functionality const pipe = (...functions) => input => functions.reduce( (acc, fn) => fn(acc), input ) // Composed functions for multiplication of specific values const multiply6 = pipe(double, triple) const multiply9 = pipe(triple, triple) const multiply16 = pipe(quadruple, quadruple) const multiply24 = pipe(double, triple, quadruple) // Usage multiply6(6) // 36 multiply9(9) // 81 multiply16(16) // 256 multiply24(10) // 240 ``` ### reduce を使って map メソッドを書く ```js if (!Array.prototype.mapUsingReduce) { Array.prototype.mapUsingReduce = function(callback, initialValue) { return this.reduce(function(mappedArray, currentValue, currentIndex, array) { mappedArray[index] = callback.call(initialValue, currentValue, currentIndex, array) return mappedArray }, []) } } [1, 2, , 3].mapUsingReduce( (currentValue, currentIndex, array) => currentValue + currentIndex + array.length ) // [5, 7, , 10] ``` ## 仕様書 {{Specifications}} ## ブラウザーの互換性 {{Compat}} ## 関連情報 - `Array.prototype.reduce` のポリフィルが [`core-js`](https://github.com/zloirock/core-js#ecmascript-array) で利用できます - {{jsxref("Array.prototype.reduceRight()")}}