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;
    };
  }

 

posted @ 2018-07-04 10:54  hahazexia  阅读(347)  评论(0)    收藏  举报