--- title: Array.prototype.reduce() slug: Web/JavaScript/Reference/Global_Objects/Array/Reduce tags: - Array - ECMAScript 5 - JavaScript - Method - Prototype - Reference translation_of: Web/JavaScript/Reference/Global_Objects/Array/Reduce --- <div>{{JSRef}}</div> <p><code><strong>reduce()</strong></code><strong> </strong>메서드는 배열의 각 요소에 대해 주어진 <strong>리듀서</strong>(reducer) 함수를 실행하고, 하나의 결과값을 반환합니다.</p> <div>{{EmbedInteractiveExample("pages/js/array-reduce.html")}}</div> <p><strong>리듀서</strong> 함수는 네 개의 인자를 가집니다.</p> <ol> <li>누산기 (acc)</li> <li>현재 값 (cur)</li> <li>현재 인덱스 (idx)</li> <li>원본 배열 (src)</li> </ol> <p>리듀서 함수의 반환 값은 누산기에 할당되고, 누산기는 순회 중 유지되므로 결국 최종 결과는 하나의 값이 됩니다.</p> <h2 id="구문">구문</h2> <pre class="syntaxbox"><var>arr</var>.reduce(<var>callback</var>[, <var>initialValue</var>])</pre> <h3 id="매개변수">매개변수</h3> <dl> <dt><code>callback</code></dt> <dd>배열의 각 요소에 대해 실행할 함수. 다음 네 가지 인수를 받습니다.</dd> <dd> <dl> <dt><code>accumulator</code></dt> <dd>누산기는 콜백의 반환값을 누적합니다. 콜백의 이전 반환값 또는, 콜백의 첫 번째 호출이면서 <code>initialValue</code>를 제공한 경우에는 <code>initialValue</code>의 값입니다.</dd> <dt><code>currentValue</code></dt> <dd>처리할 현재 요소.</dd> <dt><code>currentIndex</code> {{optional_inline}}</dt> <dd>처리할 현재 요소의 인덱스. <code>initialValue</code>를 제공한 경우 0, 아니면 1부터 시작합니다.</dd> <dt><code>array</code> {{optional_inline}}</dt> <dd><code>reduce()</code>를 호출한 배열.</dd> </dl> </dd> <dt><code>initialValue</code> {{optional_inline}}</dt> <dd><code>callback</code>의 최초 호출에서 첫 번째 인수에 제공하는 값. 초기값을 제공하지 않으면 배열의 첫 번째 요소를 사용합니다. 빈 배열에서 초기값 없이 <code>reduce()</code>를 호출하면 오류가 발생합니다.</dd> </dl> <h3 id="반환_값">반환 값</h3> <p>누적 계산의 결과 값.</p> <h2 id="설명">설명</h2> <p><code>reduce()</code>는 빈 요소를 제외하고 배열 내에 존재하는 각 요소에 대해 <code>callback</code> 함수를 한 번씩 실행하는데, 콜백 함수는 다음의 네 인수를 받습니다:</p> <ul> <li><code>accumulator</code></li> <li><code>currentValue</code></li> <li><code>currentIndex</code></li> <li><code>array</code></li> </ul> <p>콜백의 최초 호출 때 <code>accumulator</code>와 <code>currentValue</code>는 다음 두 가지 값 중 하나를 가질 수 있습니다. 만약 <code>reduce()</code> 함수 호출에서 <code>initialValue</code>를 제공한 경우, <code>accumulator</code>는 <code>initialValue</code>와 같고 <code>currentValue</code>는 배열의 첫 번째 값과 같습니다. <code>initialValue</code>를 제공하지 않았다면, <code>accumulator</code>는 배열의 첫 번째 값과 같고 <code>currentValue</code>는 두 번째와 같습니다.</p> <div class="note"> <p><strong>참고:</strong> <code>initialValue</code>를 제공하지 않으면, <code>reduce()</code>는 인덱스 1부터 시작해 콜백 함수를 실행하고 첫 번째 인덱스는 건너 뜁니다. <code>initialValue</code>를 제공하면 인덱스 0에서 시작합니다.</p> </div> <p>배열이 비어있는데 <code>initialValue</code>도 제공하지 않으면 {{jsxref("TypeError")}}가 발생합니다. 배열의 요소가 (위치와 관계없이) 하나 뿐이면서 <code>initialValue</code>를 제공되지 않은 경우, 또는 <code>initialValue</code>는 주어졌으나 배열이 빈 경우엔 그 단독 값을 <code>callback</code> 호출 없이 반환합니다.</p> <p>다음의 예제처럼 <code>initialValue</code>을 제공하지 않으면 출력 가능한 형식이 세 가지이므로, 보통 초기값을 주는 것이 더 안전합니다.</p> <pre class="brush: js">var maxCallback = ( acc, cur ) => Math.max( acc.x, cur.x ); var maxCallback2 = ( max, cur ) => Math.max( max, cur ); // initialValue 없이 reduce() [ { x: 22 }, { x: 42 } ].reduce( maxCallback ); // 42 [ { x: 22 } ].reduce( maxCallback ); // { x: 22 } [ ].reduce( maxCallback ); // TypeError // map/reduce로 개선 - 비었거나 더 큰 배열에서도 동작함 [ { x: 22 }, { x: 42 } ].map( el => el.x ) .reduce( maxCallback2, -Infinity );</pre> <h3 id="reduce()_작동_방식"><code>reduce()</code> 작동 방식</h3> <p>다음의 예제를 생각해 봅시다.</p> <pre class="brush: js">[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array) { return accumulator + currentValue; }); </pre> <p>콜백은 4번 호출됩니다. 각 호출의 인수와 반환값은 다음과 같습니다.</p> <table> <thead> <tr> <th scope="col"><code>callback</code></th> <th scope="col"><code>accumulator</code></th> <th scope="col"><code>currentValue</code></th> <th scope="col"><code>currentIndex</code></th> <th scope="col"><code>array</code></th> <th scope="col">반환 값</th> </tr> </thead> <tbody> <tr> <th scope="row">1번째 호출</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/애로우_펑션", "화살표 함수","",1)}}를 제공할 수도 있습니다. 아래 코드는 위의 코드와 같은 결과를 반환합니다.</p> <pre class="brush: js">[0, 1, 2, 3, 4].reduce( (prev, curr) => prev + curr ); </pre> <p><code>reduce()</code>의 두 번째 인수로 초기값을 제공하는 경우, 결과는 다음과 같습니다:</p> <pre class="brush: js">[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array) { return accumulator + currentValue; }, 10); </pre> <table> <thead> <tr> <th scope="col"> </th> <th scope="col"><code>accumulator</code></th> <th scope="col"><code>currentValue</code></th> <th scope="col"><code>currentIndex</code></th> <th scope="col"><code>array</code></th> <th scope="col">반환값</th> </tr> </thead> <tbody> <tr> <th scope="row">1번째 호출</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="예제">예제</h2> <h3 id="배열의_모든_값_합산">배열의 모든 값 합산</h3> <pre class="brush: js">var sum = [0, 1, 2, 3].reduce(function (accumulator, currentValue) { return accumulator + currentValue; }, 0); // sum is 6 </pre> <p>화살표 함수로도 작성할 수 있습니다.</p> <pre class="brush: js">var total = [ 0, 1, 2, 3 ].reduce( ( accumulator, currentValue ) => accumulator + currentValue, 0 );</pre> <h3 id="객체_배열에서의_값_합산">객체 배열에서의 값 합산</h3> <p>객체로 이루어진 배열에 들어 있는 값을 합산하기 위해서는 <strong>반드시</strong> 초기값을 주어 각 항목이 여러분의 함수를 거치도록 해야 합니다.</p> <pre class="brush: js">var initialValue = 0; var sum = [{x: 1}, {x:2}, {x:3}].reduce(function (accumulator, currentValue) { return accumulator + currentValue.x; },initialValue) console.log(sum) // logs 6 </pre> <p>화살표 함수(arrow function)로도 작성할 수 있습니다: </p> <pre class="brush: js">var initialValue = 0; var sum = [{x: 1}, {x:2}, {x:3}].reduce( (accumulator, currentValue) => accumulator + currentValue.x ,initialValue ); console.log(sum) // logs 6</pre> <h3 id="중첩_배열_펼치기flatten">중첩 배열 펼치기</h3> <pre class="brush: js">var flattened = [[0, 1], [2, 3], [4, 5]].reduce( function(accumulator, currentValue) { return accumulator.concat(currentValue); }, [] ); // 펼친 결과: [0, 1, 2, 3, 4, 5] </pre> <p>화살표 함수로도 작성할 수 있습니다:</p> <pre class="brush: js">var flattened = [[0, 1], [2, 3], [4, 5]].reduce( ( accumulator, currentValue ) => accumulator.concat(currentValue), [] ); </pre> <h3 id="객체_내의_값_인스턴스_개수_세기">객체 내의 값 인스턴스 개수 세기</h3> <pre class="brush: js">var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice']; var 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="속성으로_객체_분류하기">속성으로 객체 분류하기</h3> <pre class="brush: js">var people = [ { name: 'Alice', age: 21 }, { name: 'Max', age: 20 }, { name: 'Jane', age: 20 } ]; function groupBy(objectArray, property) { return objectArray.reduce(function (acc, obj) { var key = obj[property]; if (!acc[key]) { acc[key] = []; } acc[key].push(obj); return acc; }, {}); } var groupedPeople = groupBy(people, 'age'); // groupedPeople is: // { // 20: [ // { name: 'Max', age: 20 }, // { name: 'Jane', age: 20 } // ], // 21: [{ name: 'Alice', age: 21 }] // } </pre> <h3 id="확장_연산자와_초기값을_이용하여_객체로_이루어진_배열에_담긴_배열_연결하기">확장 연산자와 초기값을 이용하여 객체로 이루어진 배열에 담긴 배열 연결하기</h3> <pre class="brush: js">// friends - an array of objects // where object field "books" - list of favorite books var 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 var 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="배열의_중복_항목_제거">배열의 중복 항목 제거</h3> <div class="blockIndicator note"> <p>참고: {{jsxref("Set")}}과 {{jsxref("Array.from()")}}을 사용할 수 있는 환경이라면, <code>let orderedArray = Array.from(new Set(myArray));</code>를 사용해 중복 요소를 제거할 수도 있습니다.</p> </div> <pre class="brush: js">let arr = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4]; let result = arr.sort().reduce((accumulator, current) => { const length = accumulator.length if (length === 0 || accumulator[length - 1] !== current) { accumulator.push(current); } return accumulator; }, []); console.log(result); //[1,2,3,4,5] </pre> <h3 id="프로미스를_순차적으로_실행하기">프로미스를 순차적으로 실행하기</h3> <pre class="brush: 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 </pre> <h3 id="함수_구성을_위한_파이프_함수">함수 구성을 위한 파이프 함수</h3> <pre class="brush: 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 </pre> <h3 id="reduce()로_map()_작성"><code>reduce()</code>로 <code>map()</code> 작성</h3> <pre class="brush: js">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="폴리필">폴리필</h2> <pre class="brush: js">// ECMA-262의 진행 단계, 5판(Edition), 15.4.4.21 // 참조: 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> <h2 id="명세">명세</h2> <table class="standard-table"> <tbody> <tr> <th scope="col">Specification</th> <th scope="col">Status</th> <th scope="col">Comment</th> </tr> <tr> <td>{{SpecName('ES5.1', '#sec-15.4.4.21', 'Array.prototype.reduce()')}}</td> <td>{{Spec2('ES5.1')}}</td> <td>Initial definition. Implemented in JavaScript 1.8.</td> </tr> <tr> <td>{{SpecName('ES6', '#sec-array.prototype.reduce', 'Array.prototype.reduce()')}}</td> <td>{{Spec2('ES6')}}</td> <td> </td> </tr> <tr> <td>{{SpecName('ESDraft', '#sec-array.prototype.reduce', 'Array.prototype.reduce()')}}</td> <td>{{Spec2('ESDraft')}}</td> <td> </td> </tr> </tbody> </table> <h2 id="브라우저_호환성">브라우저 호환성</h2> <div> <p>{{Compat("javascript.builtins.Array.reduce")}}</p> </div> <h2 id="같이_보기">같이 보기</h2> <ul> <li>{{jsxref("Array.prototype.reduceRight()")}}</li> </ul>