--- title: Array.prototype.reduce() slug: Web/JavaScript/Reference/Global_Objects/Array/Reduce tags: - ECMAScript 5 - JavaScript - Reduce - Масив - метод translation_of: Web/JavaScript/Reference/Global_Objects/Array/Reduce ---
Метод reduce()
виконує функцію reducer (функцію вказуєте ви) для кожного елемента масиву та повертає єдине значення.
Функія reducer отримує чотири параметри:
acc
) - Акумуляторcur
) - Поточне значенняidx
) - Поточний індексsrc
) - Вхідний масивФункція reducer повертає значення та присовює його акумулятору. Значення аккумулятора запам'ятовується через усі ітерації і повертається наприкінці як єдиний результат.
arr.reduce(callback( accumulator, currentValue[, index[, array]] )[, initialValue])
callback
accumulator
callback
. В акумуляторі зберігається попереднє значення результату виклику callback
функції або initialValue
, якщо воно було надано (дивись нижче).currentValue
currentIndex
{{optional_inline}}initialValue
, інакше з 1 .array
{{optional_inline}}reduce()
.initialValue
{{optional_inline}}callback
функції. Якщо це значення не було задане, буде використано перший елемент масиву. Виклик reduce()
на порожньому масиві без вказання initial value призведе до помилки.Значення, що зберігається в акумуляторі після останньої ітерації.
reduce()
виконує callback
функцію один раз для кожного елемента масиву за виключенням дірок (порожніх елементів). Функція отримує чотири аргументи:
accumulator
currentValue
currentIndex
array
При першому виклику callback
функції accumulator
і currentValue
можуть мати одне із двох значень. Якщо значення initialValue
задане при виклику reduce()
, значення accumulator
дорівнюватиме значенню initialValue
, а currentValue
дорівнюватиме першому елементу масиву. Якщо значення initialValue
не задане, accumulator
буде рівним першому елементу масиву, а currentValue
-- другому.
Примітка: Якщо значення initialValue
не задане, reduce()
виконуватиме callback
функцію починаючи з індексу 1, пропустивши перший індекс. Якщо initialValue
задане, виконання почнеться із 0-го індексу.
Якщо масив порожній і значення initialValue
не задане, буде створено помилку {{jsxref("TypeError")}}. Якщо масив складається тільки з одного елемента (незалежно від його позиції) і значення initialValue
не задане, або якщо значення initialValue
задане, але масив порожній, буде повернуто єдине наявне значення, а функція callback
не буде викликана.
Зазвичай безпечніше вказувати початкове значення (initialValue
), адже без нього можливі три різні результати (див. приклад).
var maxCallback = ( acc, cur ) => Math.max( acc.x, cur.x ); var maxCallback2 = ( max, cur ) => Math.max( max, cur ); // reduce() без initialValue [ { 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 );
Нехай відбувся наступний виклик reduce()
:
[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array) { return accumulator + currentValue; });
callback
буде викликано чотири рази із наступними аргументами та значеннями, поверненими при кожному виклику:
callback |
accumulator |
currentValue |
currentIndex |
array |
повернене значення |
---|---|---|---|---|---|
перший виклик | 0 |
1 |
1 | [0, 1, 2, 3, 4] |
1 |
другий виклик | 1 |
2 |
2 | [0, 1, 2, 3, 4] |
3 |
третій виклик | 3 |
3 |
3 | [0, 1, 2, 3, 4] |
6 |
четвертий виклик | 6 |
4 |
4 | [0, 1, 2, 3, 4] |
10 |
Значення, повернене reduce()
буде значенням, отриманим при останньому виклику callback
(10
).
The value returned by reduce()
would be that of the last callback invocation (10
).
Можна також задати {{jsxref("Functions/Arrow_functions", "Arrow Function","",1)}} замість повної функції. Наступний код згенерує такий самий результат, як код у попередньому блоці.
[0, 1, 2, 3, 4].reduce( (accumulator, currentValue, currentIndex, array) => accumulator + currentValue );
Якщо початкове значення (initialValue)
задати як другий аргумент функції reduce()
, результат буде наступним:
[0, 1, 2, 3, 4].reduce((accumulator, currentValue, currentIndex, array) => { return accumulator + currentValue; }, 10);
callback |
accumulator |
currentValue |
currentIndex |
array |
повернене значення |
---|---|---|---|---|---|
перший виклик | 10 |
0 |
0 |
[0, 1, 2, 3, 4] |
10 |
другий виклик | 10 |
1 |
1 |
[0, 1, 2, 3, 4] |
11 |
третій виклик | 11 |
2 |
2 |
[0, 1, 2, 3, 4] |
13 |
четвертий виклик | 13 |
3 |
3 |
[0, 1, 2, 3, 4] |
16 |
п'ятий виклик |
16 |
4 |
4 |
[0, 1, 2, 3, 4] |
20 |
Значення, повернене функцією reduce()
у цьому випадку дорівнюватиме 20
.
var sum = [0, 1, 2, 3].reduce(function (accumulator, currentValue) { return accumulator + currentValue; }, 0); // sum is 6
Приклад вище, реалізований за допомогою arrow function:
var total = [ 0, 1, 2, 3 ].reduce( ( accumulator, currentValue ) => accumulator + currentValue, 0 );
Щоб підсумувати значення, що знаходяться у масиві об'єктів, Ви повинні вказати початкове значення для того, щоб для кожного із елементів була викликана Ваша функція.
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
Також за допомогою arrow function:
var initialValue = 0; var sum = [{x: 1}, {x:2}, {x:3}].reduce( (accumulator, currentValue) => accumulator + currentValue.x ,initialValue ); console.log(sum) // logs 6
Сплющення (flattening) - процес, у результаті якого із масиву масивів отримуємо один масив, що містить елементи усіх вкладених масивів.
var flattened = [[0, 1], [2, 3], [4, 5]].reduce( function(accumulator, currentValue) { return accumulator.concat(currentValue); }, [] ); // flattened is [0, 1, 2, 3, 4, 5]
Також за допомогою arrow function:
var flattened = [[0, 1], [2, 3], [4, 5]].reduce( ( accumulator, currentValue ) => accumulator.concat(currentValue), [] );
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 }
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 }] // }
// 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' // ]
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]
/** * 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
// 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
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]
// 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; } }); }
If you need to support truly obsolete JavaScript engines that don't support Object.defineProperty()
, it's best not to polyfill Array.prototype
methods at all, as you can't make them non-enumerable.
Specification | Status | Comment |
---|---|---|
{{SpecName('ES5.1', '#sec-15.4.4.21', 'Array.prototype.reduce()')}} | {{Spec2('ES5.1')}} | Initial definition. Implemented in JavaScript 1.8. |
{{SpecName('ES6', '#sec-array.prototype.reduce', 'Array.prototype.reduce()')}} | {{Spec2('ES6')}} | |
{{SpecName('ESDraft', '#sec-array.prototype.reduce', 'Array.prototype.reduce()')}} | {{Spec2('ESDraft')}} |
{{Compat("javascript.builtins.Array.reduce")}}