Array.prototype.reduce()和Array.prototype.reduceRight()
Array.prototype.reduce()
reduce()方法对累加器和数组中的每个元素(从左到右)应用一个函数,将其减少为单个值。
此方法不改变原数组。
arr.reduce(callback[, initialValue])
callback
执行数组中每个值的函数,包含四个参数:
accumulator
累加器累加回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue(如下所示)。
currentValue
数组中正在处理的元素。
currentIndex可选
数组中正在处理的当前元素的索引。 如果提供了initialValue,则索引号为0,否则为索引为1。
array可选
调用reduce的数组
initialValue可选
用作第一个调用 callback的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。
返回值是累加器变量。
reduce为数组中的每一个元素依次执行callback函数,不包括数组中被删除或从未被赋值的元素
回调函数第一次执行时,accumulator 和currentValue的取值有两种情况:调用reduce时提供initialValue,accumulator取值为initialValue,currentValue取数组中的第一个值;没有提供 initialValue,accumulator取数组中的第一个值,currentValue取数组中的第二个值。
注意:如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始。
如果数组为空且没有提供initialValue,会抛出TypeError 。如果数组仅有一个元素(无论位置如何)并且没有提供initialValue, 或者有提供initialValue但是数组为空,那么此唯一值将被返回并且callback不会被执行。
所以提供initialValue会更安全。
下面的例子,如果没有提供initialValue,则可能有三种输出:
var maxCallback = ( pre, cur ) => Math.max( pre.x, cur.x ); var maxCallback2 = ( max, cur ) => Math.max( max, cur ); // reduce() without initialValue [ { x: 22 }, { x: 42 } ].reduce( maxCallback ); // 42 [ { x: 22 } ].reduce( maxCallback ); // { x: 22 } [ ].reduce( maxCallback ); // TypeError // map/reduce; better solution, also works for empty arrays [ { x: 22 }, { x: 42 } ].map( el => el.x ) .reduce( maxCallback2, -Infinity );
将二维数组转化为一维
var flattened = [[0, 1], [2, 3], [4, 5]].reduce( function(a, b) { return a.concat(b); }, [] ); // flattened is [0, 1, 2, 3, 4, 5]
计算数组中每个元素出现的次数
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 }
使用扩展运算符和initialValue绑定包含在对象数组中的数组
// 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(prev, curr) { return [...prev, ...curr.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((init, current)=>{ if(init.length===0 || init[init.length-1]!==current){ init.push(current); } return init; }, []); console.log(result); //[1,2,3,4,5]
polyfill
// 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) {//数组为null抛错误 throw new TypeError( 'Array.prototype.reduce ' + 'called on null or undefined' ); } if (typeof callback !== 'function') {//callback不是函数抛错误 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) {//initialValue值 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) {//如果数组长度为0且没有指定initialValue抛错误 throw new TypeError( 'Reduce of empty array ' + 'with no initial value' ); } value = o[k++];//否则累加器第一个值等于array[0] } // 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);//执行callback给累加器赋值 } // d. Increase k by 1. k++; } // 9. Return accumulator. return value; } }); }
Array.prototype.reduceRight()
reduceRight() 方法接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将其减少为单个值。
此方法不改变原数组。
reduceRight()和reduce()的区别是,reduceRight循环数组的方向是反向的,从右到左。
首次调用回调函数时,previousValue 和 currentValue 可以是两个值之一。如果调用 reduceRight 时提供了 initialValue 参数,则 previousValue 等于 initialValue,currentValue 等于数组中的最后一个值。如果没有提供 initialValue 参数,则 previousValue 等于数组最后一个值, currentValue 等于数组中倒数第二个值。
例子:reduce 与 reduceRight 之间的区别
var a = ['1', '2', '3', '4', '5']; var left = a.reduce(function(prev, cur) { return prev + cur; }); var right = a.reduceRight(function(prev, cur) { return prev + cur; }); console.log(left); // "12345" console.log(right); // "54321"
polyfill
// Production steps of ECMA-262, Edition 5, 15.4.4.22 // Reference: http://es5.github.io/#x15.4.4.22 if ('function' !== typeof Array.prototype.reduceRight) { Array.prototype.reduceRight = function(callback /*, initialValue*/) { 'use strict'; if (null === this || 'undefined' === typeof this) {//如果数组为null抛出错误 throw new TypeError('Array.prototype.reduce called on null or undefined'); } if ('function' !== typeof callback) {//callback不是函数抛出错误 throw new TypeError(callback + ' is not a function'); } var t = Object(this), len = t.length >>> 0, k = len - 1, value;//t数组,len数组长度,k起始索引(数组最后一个值索引),value累加器初始值 if (arguments.length >= 2) {//如果有第二个参数就是累加器初始值 value = arguments[1]; } else { while (k >= 0 && !(k in t)) { k--; } if (k < 0) {//如果是空数组却没有initialValue抛错误 throw new TypeError('Reduce of empty array with no initial value'); } value = t[k--];//否则initialValue为最后一个值,第一次循环k索引为倒数第二个 } for (; k >= 0; k--) { if (k in t) { value = callback(value, t[k], k, t);//循环并为累加器赋值 } } return value; }; }