diff options
author | Masahiro FUJIMOTO <mfujimot@gmail.com> | 2021-09-11 15:36:23 +0900 |
---|---|---|
committer | Masahiro FUJIMOTO <mfujimot@gmail.com> | 2021-09-19 23:56:26 +0900 |
commit | 68a0467b225cdcf072526490374d29a21977e8a1 (patch) | |
tree | 7237841cce7bf4d4c7749f83bf08aa277a9e8191 /files/ja/web/javascript | |
parent | 4977058de113f2a98f641fce2cf4f08e796985be (diff) | |
download | translated-content-68a0467b225cdcf072526490374d29a21977e8a1.tar.gz translated-content-68a0467b225cdcf072526490374d29a21977e8a1.tar.bz2 translated-content-68a0467b225cdcf072526490374d29a21977e8a1.zip |
Global_Objects/Array/Reduce を更新
- 2021/08/16 時点の英語版に同期
Diffstat (limited to 'files/ja/web/javascript')
-rw-r--r-- | files/ja/web/javascript/reference/global_objects/array/reduce/index.html | 590 | ||||
-rw-r--r-- | files/ja/web/javascript/reference/global_objects/array/reduce/index.md | 549 |
2 files changed, 549 insertions, 590 deletions
diff --git a/files/ja/web/javascript/reference/global_objects/array/reduce/index.html b/files/ja/web/javascript/reference/global_objects/array/reduce/index.html deleted file mode 100644 index bc07a5506e..0000000000 --- a/files/ja/web/javascript/reference/global_objects/array/reduce/index.html +++ /dev/null @@ -1,590 +0,0 @@ ---- -title: Array.prototype.reduce() -slug: Web/JavaScript/Reference/Global_Objects/Array/reduce -tags: - - Array - - Array method - - ECMAScript 5 - - JavaScript - - Method - - Prototype - - Reduce - - Reference - - メソッド -translation_of: Web/JavaScript/Reference/Global_Objects/Array/Reduce ---- -<div>{{JSRef}}</div> - -<p><code><strong>reduce()</strong></code> メソッドは、配列の各要素に対して (引数で与えられた) <strong>reducer</strong> 関数を実行して、単一の出力値を生成します。</p> - -<div>{{EmbedInteractiveExample("pages/js/array-reduce.html")}}</div> - -<div class="hidden">このデモのソースファイルは GitHub リポジトリに格納されています。デモプロジェクトに協力したい場合は、<a href="https://github.com/mdn/interactive-examples">https://github.com/mdn/interactive-examples</a> をクローンしてプルリクエストを送信してください。</div> - -<p><strong>reducer</strong> 関数は 4 つの引数を取ります。</p> - -<ol> - <li>アキュムレーター (<code><var>acc</var></code>)</li> - <li>現在値 (<code><var>cur</var></code>)</li> - <li>現在の添字 (<code><var>idx</var></code>)</li> - <li>元の配列 (<code><var>src</var></code>)</li> -</ol> - -<p><strong>reducer</strong> 関数の返値はアキュムレーターに代入され、配列内の各反復に対してこの値を記憶します。最終的に単一の結果値になります。</p> - -<h2 id="Syntax" name="Syntax">構文</h2> - -<pre class="syntaxbox notranslate"><var>arr</var>.reduce(<var>callback</var>( <var>accumulator</var>, <var>currentValue</var>[, <var>index</var>[, <var>array</var>]] ) { - // return result from executing something for <var>accumulator</var> or <var>currentValue</var> -}[, <var>initialValue</var>]);</pre> - -<h3 id="Parameters" name="Parameters">引数</h3> - -<dl> - <dt><code><var>callback</var></code></dt> - <dd> - <p>配列のすべての要素 (<code><var>initialValue</var></code> が提供されなかった場合は、最初を除く) に対して実行される関数です。</p> - - <p>これは 4 つの引数を取ります。</p> - - <dl> - <dt><code><var>accumulator</var></code></dt> - <dd><var>callback</var> の返値を蓄積するアキュームレーターです。これは、コールバックの前回の呼び出しで返された値、あるいは <code><var>initialValue</var></code> が指定されている場合はその値となります (以下を参照ください)。</dd> - <dt><code><var>currentValue</var></code></dt> - <dd>現在処理されている配列の要素です。</dd> - <dt><code><var>index</var></code> {{optional_inline}}</dt> - <dd>現在処理されている配列要素のインデックスです。<code>initialValue</code> が指定された場合はインデックス <code>0</code> から、そうでない場合はインデックス <code>1</code> から開始します。</dd> - <dt><code><var>array</var></code> {{optional_inline}}</dt> - <dd><code>reduce()</code> が呼び出された配列です。</dd> - </dl> - </dd> - <dt><code><var>initialValue</var></code> {{optional_inline}}</dt> - <dd><code><var>callback</var></code> の最初の呼び出しの最初の引数として使用する値。<code><var>initialValue</var></code> が与えられなかった場合、配列の最初の要素がアキュムレーターの初期値として使用され、<code><var>currentValue</var></code> としてスキップされます。空の配列に対して <code>reduce()</code> を呼び出した際、<code><var>initialValue</var></code> が指定されていないと {{jsxref("TypeError")}} が発生します。</dd> -</dl> - -<h3 id="Return_value" name="Return_value">返値</h3> - -<p>畳み込みによって得られた 1 つの値です。</p> - -<h2 id="Description" name="Description">解説</h2> - -<p><code>reduce()</code> メソッドは、配列に存在する各要素ごとに対して、<code>callback</code> 関数を次の 4 つの引数を渡しながら一度だけ実行します。</p> - -<ol> - <li><code><var>accumulator</var></code></li> - <li><code><var>currentValue</var></code></li> - <li><code><var>currentIndex</var></code></li> - <li><code><var>array</var></code></li> -</ol> - -<p>コールバックが最初に呼び出されたとき、<code><var>accumulator</var></code> と <code><var>currentValue</var></code> が 2 つの値になります。<code><var>initialValue</var></code> が <code>reduce()</code> の呼び出しで提供されていた場合、<code><var>accumulator</var></code> は <code><var>initialValue</var></code> と等しくなり、<code><var>currentValue</var></code> は配列の最初の値と等しくなります。<code><var>initialValue</var></code> が提供されなかった場合は、<code><var>accumulator</var></code> は配列の最初の値と等しくなり、<code><var>currentValue</var></code> は 2番目の値と等しくなります。</p> - -<div class="note"> -<p><strong>注:</strong> <code><var>initialValue</var></code> が指定されなかった場合は、<code>reduce()</code> は最初の要素を飛ばしてインデックス <code>1</code> から実行されます。<code><var>initialValue</var></code> が指定されていたらインデックス <code>0</code> から開始します。</p> -</div> - -<p>配列が空で <code><var>initialValue</var></code> が指定されなかった場合は {{jsxref("TypeError")}} が発生します。</p> - -<p>配列が (位置に関わらず) 1 つの要素しか持たず、<code><var>initialValue</var></code> が指定されなかった場合、または <code><var>initialValue</var></code> が指定されていても配列が空だった場合、<em><code>callback</code> が実行されずに</em>要素が返却されます。</p> - -<p><code><var>initialValue</var></code> を指定しなかった場合、下記の例のような 4 種類の結果が発生しうるため、<code><var>initialValue</var></code> を指定する方が通常は安全です。</p> - -<pre class="brush: js notranslate">let maxCallback = ( acc, cur ) => Math.max( acc.x, cur.x ); -let maxCallback2 = ( max, cur ) => Math.max( max, cur ); - -// reduce without initialValue -[ { x: 2 }, { x: 22 }, { x: 42 } ].reduce( maxCallback ); // NaN -[ { x: 2 }, { x: 22 } ].reduce( maxCallback ); // 22 -[ { x: 2 } ].reduce( maxCallback ); // { x: 2 } -[ ].reduce( maxCallback ); // TypeError - -// map & reduce with initialValue; better solution, also works for empty or larger arrays -[ { x: 22 }, { x: 42 } ].map( el => el.x ) - .reduce( maxCallback2, -Infinity ); -</pre> - -<h3 id="How_reduce_works" name="How_reduce_works">reduce() はどう動くか</h3> - -<p>次のコードのように <code>reduce()</code> を使用した場合について見てみましょう。</p> - -<pre class="brush: js notranslate">[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array) { - return accumulator + currentValue -}) -</pre> - -<p>コールバック関数は 4 回呼び出され、各回の引数の内容は以下のようになっています。</p> - -<table class="standard-table"> - <thead> - <tr> - <th scope="col"><code><var>callback</var></code> の反復</th> - <th scope="col"><code><var>accumulator</var></code></th> - <th scope="col"><code><var>currentValue</var></code></th> - <th scope="col"><code><var>currentIndex</var></code></th> - <th scope="col"><code><var>array</var></code></th> - <th scope="col">返値</th> - </tr> - </thead> - <tbody> - <tr> - <th scope="row">初回の呼出し</th> - <td><code>0</code></td> - <td><code>1</code></td> - <td><code>1</code></td> - <td><code>[0, 1, 2, 3, 4]</code></td> - <td><code>1</code></td> - </tr> - <tr> - <th scope="row">2 回目の呼出し</th> - <td><code>1</code></td> - <td><code>2</code></td> - <td><code>2</code></td> - <td><code>[0, 1, 2, 3, 4]</code></td> - <td><code>3</code></td> - </tr> - <tr> - <th scope="row">3 回目の呼出し</th> - <td><code>3</code></td> - <td><code>3</code></td> - <td><code>3</code></td> - <td><code>[0, 1, 2, 3, 4]</code></td> - <td><code>6</code></td> - </tr> - <tr> - <th scope="row">4 回目の呼出し</th> - <td><code>6</code></td> - <td><code>4</code></td> - <td><code>4</code></td> - <td><code>[0, 1, 2, 3, 4]</code></td> - <td><code>10</code></td> - </tr> - </tbody> -</table> - -<p><code>reduce()</code> の返値は、コールバック呼び出しの最後の返値である (<code>10</code>) となるでしょう。</p> - -<p>通常の関数の代わりに{{jsxref("Functions/Arrow_functions", "アロー関数","",1)}}を指定することができます。下記のコードは上述のコードと同じ結果を返します。</p> - -<pre class="brush: js notranslate">[0, 1, 2, 3, 4].reduce( (accumulator, currentValue, currentIndex, array) => accumulator + currentValue ) -</pre> - -<p><code>reduce()</code> の 2 つ目の引数に初期値を設定した場合は、コールバック各回の内部的な動作はこのようになります。</p> - -<pre class="brush: js notranslate">[0, 1, 2, 3, 4].reduce((accumulator, currentValue, currentIndex, array) => { - return accumulator + currentValue -}, 10) -</pre> - -<table class="standard-table"> - <thead> - <tr> - <th scope="col"><code><var>callback</var></code> の反復</th> - <th scope="col"><code><var>accumulator</var></code></th> - <th scope="col"><code><var>currentValue</var></code></th> - <th scope="col"><code><var>currentIndex</var></code></th> - <th scope="col"><code><var>array</var></code></th> - <th scope="col">返値</th> - </tr> - </thead> - <tbody> - <tr> - <th scope="row">初回の呼出し</th> - <td><code>10</code></td> - <td><code>0</code></td> - <td><code>0</code></td> - <td><code>[0, 1, 2, 3, 4]</code></td> - <td><code>10</code></td> - </tr> - <tr> - <th scope="row">2 回目の呼出し</th> - <td><code>10</code></td> - <td><code>1</code></td> - <td><code>1</code></td> - <td><code>[0, 1, 2, 3, 4]</code></td> - <td><code>11</code></td> - </tr> - <tr> - <th scope="row">3 回目の呼出し</th> - <td><code>11</code></td> - <td><code>2</code></td> - <td><code>2</code></td> - <td><code>[0, 1, 2, 3, 4]</code></td> - <td><code>13</code></td> - </tr> - <tr> - <th scope="row">4 回目の呼出し</th> - <td><code>13</code></td> - <td><code>3</code></td> - <td><code>3</code></td> - <td><code>[0, 1, 2, 3, 4]</code></td> - <td><code>16</code></td> - </tr> - <tr> - <th scope="row">5 回目の呼出し</th> - <td><code>16</code></td> - <td><code>4</code></td> - <td><code>4</code></td> - <td><code>[0, 1, 2, 3, 4]</code></td> - <td><code>20</code></td> - </tr> - </tbody> -</table> - -<p>この場合の <code>reduce()</code> の返値は <code>20</code> となります。</p> - -<h2 id="Polyfill" name="Polyfill">ポリフィル</h2> - -<pre class="brush: js notranslate">// Production steps of ECMA-262, Edition 5, 15.4.4.21 -// Reference: http://es5.github.io/#x15.4.4.21 -// https://tc39.github.io/ecma262/#sec-array.prototype.reduce -if (!Array.prototype.reduce) { - Object.defineProperty(Array.prototype, 'reduce', { - value: function(callback /*, initialValue*/) { - if (this === null) { - throw new TypeError( 'Array.prototype.reduce ' + - 'called on null or undefined' ); - } - if (typeof callback !== 'function') { - throw new TypeError( callback + - ' is not a function'); - } - - // 1. Let O be ? ToObject(this value). - var o = Object(this); - - // 2. Let len be ? ToLength(? Get(O, "length")). - var len = o.length >>> 0; - - // Steps 3, 4, 5, 6, 7 - var k = 0; - var value; - - if (arguments.length >= 2) { - value = arguments[1]; - } else { - while (k < len && !(k in o)) { - k++; - } - - // 3. If len is 0 and initialValue is not present, - // throw a TypeError exception. - if (k >= len) { - throw new TypeError( 'Reduce of empty array ' + - 'with no initial value' ); - } - value = o[k++]; - } - - // 8. Repeat, while k < len - while (k < len) { - // a. Let Pk be ! ToString(k). - // b. Let kPresent be ? HasProperty(O, Pk). - // c. If kPresent is true, then - // i. Let kValue be ? Get(O, Pk). - // ii. Let accumulator be ? Call( - // callbackfn, undefined, - // « accumulator, kValue, k, O »). - if (k in o) { - value = callback(value, o[k], k, o); - } - - // d. Increase k by 1. - k++; - } - - // 9. Return accumulator. - return value; - } - }); -} -</pre> - -<div class="blockIndicator note"> -<p><strong>警告:</strong> {{jsxref("Object.defineProperty()")}} に対応していない本当に時代遅れの JavaScript エンジンに対応する必要がある場合、<code>Array.prototype</code> を<strong>列挙不可能</strong>にすることはできないので、メソッドのポリフィルを使わない方が良いでしょう。</p> -</div> - -<h2 id="Examples" name="Examples">例</h2> - -<h3 id="Sum_all_the_values_of_an_array" name="Sum_all_the_values_of_an_array">配列内の値の合計値を出す</h3> - -<pre class="brush: js notranslate">let sum = [0, 1, 2, 3].reduce(function (accumulator, currentValue) { - return accumulator + currentValue -}, 0) -// sum is 6 - -</pre> - -<p>また、アロー関数を用いて書くことも出来ます。</p> - -<pre class="brush: js notranslate">let total = [ 0, 1, 2, 3 ].reduce( - ( accumulator, currentValue ) => accumulator + currentValue, - 0 -)</pre> - -<h3 id="Sum_of_values_in_an_object_array" name="Sum_of_values_in_an_object_array">オブジェクトの配列の値の合計値を出す</h3> - -<p>オブジェクトの配列に含まれた値の合計値を出すには、すべての項目を関数内で取得できるようにするために <code><var>initialValue</var></code> を指定する<strong>必要があります</strong>。</p> - -<pre class="brush: js notranslate">let initialValue = 0 -let sum = [{x: 1}, {x: 2}, {x: 3}].reduce(function (accumulator, currentValue) { - return accumulator + currentValue.x -}, initialValue) - -console.log(sum) // logs 6 -</pre> - -<p>また、アロー関数を用いて書くことも出来ます。</p> - -<pre class="brush: js notranslate">let initialValue = 0 -let sum = [{x: 1}, {x: 2}, {x: 3}].reduce( - (accumulator, currentValue) => accumulator + currentValue.x - , initialValue -) - -console.log(sum) // logs 6</pre> - -<h3 id="Flatten_an_array_of_arrays" name="Flatten_an_array_of_arrays">二次元配列を一次元配列にする</h3> - -<pre class="brush: js notranslate">let flattened = [[0, 1], [2, 3], [4, 5]].reduce( - function(accumulator, currentValue) { - return accumulator.concat(currentValue) - }, - [] -) -// flattened is [0, 1, 2, 3, 4, 5] -</pre> - -<p>また、アロー関数を用いて書くことも出来ます。</p> - -<pre class="brush: js notranslate">let flattened = [[0, 1], [2, 3], [4, 5]].reduce( - ( accumulator, currentValue ) => accumulator.concat(currentValue), - [] -) -</pre> - -<h3 id="Counting_instances_of_values_in_an_object" name="Counting_instances_of_values_in_an_object">オブジェクトの値のインスタンスを数える</h3> - -<pre class="brush: js notranslate">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 } -</pre> - -<h3 id="Grouping_objects_by_a_property" name="Grouping_objects_by_a_property">プロパティによってオブジェクトをグループ化する</h3> - -<pre class="brush: js notranslate">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 }] -// } -</pre> - -<h3 id="Bonding_arrays_contained_in_an_array_of_objects_using_the_spread_operator_and_initialValue" name="Bonding_arrays_contained_in_an_array_of_objects_using_the_spread_operator_and_initialValue">スプレッド演算子と initialValue を使ってオブジェクトの配列に含まれる配列を結合させる</h3> - -<pre class="brush: js notranslate">// 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(accumulator, currentValue) { - return [...accumulator, ...currentValue.books] -}, ['Alphabet']) - -// allbooks = [ -// 'Alphabet', 'Bible', 'Harry Potter', 'War and peace', -// 'Romeo and Juliet', 'The Lord of the Rings', -// 'The Shining' -// ]</pre> - -<h3 id="Remove_duplicate_items_in_array" name="Remove_duplicate_items_in_array">配列内の重複要素を除去する</h3> - -<div class="blockIndicator note"> -<p><strong>注:</strong> {{jsxref("Set")}} と {{jsxref("Array.from()")}} に対応している環境を使っている場合、<code>let orderedArray = Array.from(new Set(myArray))</code> を使うことで重複要素を除去された配列を取得することができます。</p> -</div> - -<pre class="brush: js notranslate">let myArray = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd'] -let myOrderedArray = myArray.reduce(function (accumulator, currentValue) { - if (accumulator.indexOf(currentValue) === -1) { - accumulator.push(currentValue) - } - return accumulator -}, []) - -console.log(myOrderedArray)</pre> - -<h3 id="Replace_.filter.map_with_.reduce" name="Replace_.filter.map_with_.reduce">.filter().map() を .reduce() で置き換える</h3> - -<p>{{jsxref("Array.filter()")}} を使用した後で {{jsxref("Array.map()")}} を使用すると配列を二度走査しますが、{{jsxref("Array.reduce()")}} では同じ効果を一度の操作で実現することができ、もっと効率的です。(for ループが好きなのであれば、{{jsxref("Array.forEach()")}} で一度の操作で filter と map を行うことができます)。</p> - -<pre class="brush: js notranslate">const numbers = [-5, 6, 2, 0,]; - -const doubledPositiveNumbers = numbers.reduce((accumulator, currentValue) => { - if (currentValue > 0) { - const doubled = currentValue * 2; - accumulator.push(doubled); - } - return accumulator; -}, []); - -console.log(doubledPositiveNumbers); // [12, 4]</pre> - -<h3 id="Running_Promises_in_Sequence" name="Running_Promises_in_Sequence">シーケンス上の Promise を動かす</h3> - -<pre class="brush: js notranslate">/** - * 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 -</pre> - -<h3 id="Function_composition_enabling_piping" name="Function_composition_enabling_piping">パイプによって関数を合成する</h3> - -<pre class="brush: js notranslate">// 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 - -</pre> - -<h3 id="write_map_using_reduce" name="write_map_using_reduce">reduce を使って map メソッドを書く</h3> - -<pre class="brush: js notranslate">if (!Array.prototype.mapUsingReduce) { - Array.prototype.mapUsingReduce = function(callback, thisArg) { - return this.reduce(function(mappedArray, currentValue, index, array) { - mappedArray[index] = callback.call(thisArg, currentValue, index, array) - return mappedArray - }, []) - } -} - -[1, 2, , 3].mapUsingReduce( - (currentValue, index, array) => currentValue + index + array.length -) // [5, 7, , 10] - -</pre> - -<h2 id="Specifications" name="Specifications">仕様</h2> - -<table class="standard-table"> - <thead> - <tr> - <th scope="col">仕様書</th> - </tr> - </thead> - <tbody> - <tr> - <td>{{SpecName('ESDraft', '#sec-array.prototype.reduce', 'Array.prototype.reduce()')}}</td> - </tr> - </tbody> -</table> - -<h2 id="Browser_compatibility" name="Browser_compatibility">ブラウザーの互換性</h2> - -<div> -<p>{{Compat("javascript.builtins.Array.reduce")}}</p> -</div> - -<h2 id="See_also" name="See_also">関連情報</h2> - -<ul> - <li>{{jsxref("Array.prototype.reduceRight()")}}</li> -</ul> diff --git a/files/ja/web/javascript/reference/global_objects/array/reduce/index.md b/files/ja/web/javascript/reference/global_objects/array/reduce/index.md new file mode 100644 index 0000000000..86a45268bf --- /dev/null +++ b/files/ja/web/javascript/reference/global_objects/array/reduce/index.md @@ -0,0 +1,549 @@ +--- +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 回呼び出され、各回の引数の内容は以下のようになります。 + +<table class="standard-table"> + <thead> + <tr> + <th scope="col"> + <code><var>callback</var></code> の反復処理 + </th> + <th scope="col"> + <code><var>previousValue</var></code> + </th> + <th scope="col"> + <code><var>currentValue</var></code> + </th> + <th scope="col"> + <code><var>currentIndex</var></code> + </th> + <th scope="col"> + <code><var>array</var></code> + </th> + <th scope="col">返値</th> + </tr> + </thead> + <tbody> + <tr> + <th scope="row">初回の呼出し</th> + <td><code>0</code></td> + <td><code>1</code></td> + <td><code>1</code></td> + <td><code>[0, 1, 2, 3, 4]</code></td> + <td><code>1</code></td> + </tr> + <tr> + <th scope="row">2 回目の呼出し</th> + <td><code>1</code></td> + <td><code>2</code></td> + <td><code>2</code></td> + <td><code>[0, 1, 2, 3, 4]</code></td> + <td><code>3</code></td> + </tr> + <tr> + <th scope="row">3 回目の呼出し</th> + <td><code>3</code></td> + <td><code>3</code></td> + <td><code>3</code></td> + <td><code>[0, 1, 2, 3, 4]</code></td> + <td><code>6</code></td> + </tr> + <tr> + <th scope="row">4 回目の呼出し</th> + <td><code>6</code></td> + <td><code>4</code></td> + <td><code>4</code></td> + <td><code>[0, 1, 2, 3, 4]</code></td> + <td><code>10</code></td> + </tr> + </tbody> +</table> + +`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) +``` + +<table class="standard-table"> + <thead> + <tr> + <th scope="col"> + <code><var>callback</var></code> の反復処理 + </th> + <th scope="col"> + <code><var>previousValue</var></code> + </th> + <th scope="col"> + <code><var>currentValue</var></code> + </th> + <th scope="col"> + <code><var>currentIndex</var></code> + </th> + <th scope="col"> + <code><var>array</var></code> + </th> + <th scope="col">返値</th> + </tr> + </thead> + <tbody> + <tr> + <th scope="row">初回の呼出し</th> + <td><code>10</code></td> + <td><code>0</code></td> + <td><code>0</code></td> + <td><code>[0, 1, 2, 3, 4]</code></td> + <td><code>10</code></td> + </tr> + <tr> + <th scope="row">2 回目の呼出し</th> + <td><code>10</code></td> + <td><code>1</code></td> + <td><code>1</code></td> + <td><code>[0, 1, 2, 3, 4]</code></td> + <td><code>11</code></td> + </tr> + <tr> + <th scope="row">3 回目の呼出し</th> + <td><code>11</code></td> + <td><code>2</code></td> + <td><code>2</code></td> + <td><code>[0, 1, 2, 3, 4]</code></td> + <td><code>13</code></td> + </tr> + <tr> + <th scope="row">4 回目の呼出し</th> + <td><code>13</code></td> + <td><code>3</code></td> + <td><code>3</code></td> + <td><code>[0, 1, 2, 3, 4]</code></td> + <td><code>16</code></td> + </tr> + <tr> + <th scope="row">5 回目の呼出し</th> + <td><code>16</code></td> + <td><code>4</code></td> + <td><code>4</code></td> + <td><code>[0, 1, 2, 3, 4]</code></td> + <td><code>20</code></td> + </tr> + </tbody> +</table> + +この場合の `reduce()` の返値は `20` となります。</p> + +## 例 + +### 配列内の値の合計値を出す + +```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()")}} |