Array.prototype.reduce()

简介: Array.prototype.reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。

Array.prototype.reduce

JavaScript-Array-Reduce.jpeg


归并类方法 【MDN Docs】


reduce() 方法接收一个函数作为 累加器(accumulator) ,数组中的每个值(从左到右)开始缩减,最终为一个值。


reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,


reducer 函数的返回值分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。


接受四个参数: 上一次回调函数的返回值(或者初始值)当前元素值当前索引调用 reduce 的数组


回调函数第一次执行时,accumulatorcurrentValue的取值有两种情况:

  • 如果调用reduce()时提供了initialValueaccumulator取值为initialValuecurrentValue取数组中的第一个值;
  • 如果没有提供 initialValue,那么accumulator取数组中的第一个值,currentValue取数组中的第二个值。


所以,如果没有提供 initialValue ,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。


如果提供 initialValue ,从索引0开始。


如果数组为空且没有提供 initialValue ,会抛出TypeError 。


如果数组仅有一个元素(无论位置如何)并且没有提供 initialValue , 或者有提供 initialValue 但是数组为空,那么此唯一值将被返回并且callback不会被执行。


一般来说,提供初始值通常更安全。


constmaxCallback= (acc, cur) => {
console.log(acc, cur);
return { x: Math.max(acc.x, cur.x) };
};
constmaxCallback1= (acc, cur) => {
console.log(acc, cur);
returnMath.max(acc.x, cur.x);
};
constmaxCallback2= (acc, cur) => {
console.log(acc, cur);
returnMath.max(acc, cur);
};
// reduce() 没有初始值// 注意分配给累加器的返回值的形式[{ x: 2 }, { x: 22 }, { x: 42 }].reduce(maxCallback1); // NaN[{ x: 2 }, { x: 22 }].reduce(maxCallback1); // 22[{ x: 2 }].reduce(maxCallback1); // { x: 2 }[].reduce(maxCallback1); // Uncaught TypeError: Reduce of empty array with no initial value// map/reduce; 这是更好的方案,即使传入空数组或更大数组也可正常执行[{ x: 22 }, { x: 42 }].map((el) =>el.x).reduce(maxCallback2, -Infinity);



1 求和

constsumlist= [1, 3, 5, 7, 9, 11, 13];
constinitVal=0;
// 第二次循环方法中prev的值,是第一次循环方法返回的结果。functionitercb(prev, curVal, curIndex, arr) {
// console.log("先前值:", prev);// console.log("累加值:", curVal, ", 下标:", curIndex);returnprev+curVal;
}
consttotal=sumlist.reduce(itercb, initVal);
console.log("sum total:", total);



2 滤重

constrepeatList= ["a", "aa", "aaa", "a", "a", "aa", "aaa", "aaaa", "aaa"];
functioncbRepeat(prev, curVal, curIndex) {
if (prev.includes(curVal)) {
// 若包含,则不添加returnprev;
  } else {
// 添加return [...prev, curVal];
  }
}
constresult=repeatList.reduce(cbRepeat, []);
console.log("result:", result); // ['a', 'aa', 'aaa', 'aaaa']// anotherconstarr= [1, 2, 3, 4, 4, 1];
constnewArr=arr.reduce((prev, cur) => {
if (prev.indexOf(cur) ===-1) {
returnprev.concat(cur);
  } else {
returnprev;
  }
}, []);
console.log(newArr); // [1, 2, 3, 4]

各滤重方法的性能疑问

constmyArray= ["a", "b", "a", "b", "c", "e", "e", "c", "d", "d", "d", "d"];
console.time("indexOf");
letmyOrderedArray=myArray.reduce(function (accumulator, currentValue) {
if (accumulator.indexOf(currentValue) ===-1) {
accumulator.push(currentValue);
  }
returnaccumulator;
}, []);
console.timeEnd("indexOf"); // indexOf: 0.02685546875 msconsole.log(myOrderedArray);
console.time("sort");
letresult=myArray.sort().reduce((init, current) => {
if (init.length===0||init[init.length-1] !==current) {
init.push(current);
  }
returninit;
}, []);
console.timeEnd("sort"); // sort: 0.06787109375 msconsole.log(result); // [1,2,3,4,5]console.time("set");
letorderedArray=Array.from(newSet(myArray));
console.timeEnd("set"); // set: 0.006103515625 msconsole.log(orderedArray);


3 计数

constnames= ["Alice", "Bob", "Tiff", "Bruce", "Alice"];
// {Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}constnameNum=names.reduce((prev, cur) => {
// in 如果指定的属性在指定的对象或其原型链中,返回true// in 右操作数必须是一个对象值,可以是 new String(),但不能是 '',删除delete和赋值undefined是不一样的结果if (curinprev) {
prev[cur]++;
  } else {
prev[cur] =1;
  }
returnprev;
}, {});
console.log(nameNum); //{Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}

【in 操作符】 

in运算符用来判断对象是否拥有给定属性。

类比includes,判断数组是否拥有给定值。


【MDN 表达式和运算符】



for ... in & for ... of

constarr= ["a", "b", "c"];
constobj= { a: "aaa", b: "bbb", c: "ccc" };
for (letiinarr) {
console.log(i); // 下标,索引值 0,1,2}
for (letiofarr) {
console.log(i); // 元素值 a,b,c}
for (letiinobj) {
console.log(i); // name,非value a,b,c}
for (letiofobj) {
console.log(i); // 报错,obj is not iterable}
// 总结:// 对象只能用 for in 遍历,数组可以用 for in 和 for of 遍历// for in 遍历能获取 index 或 name,for of 遍历能拿到数组元素值// 只能foa 不能foo// fia fio 皆可,都是获取索引(index或name)



4 对象里的属性求和

varresult= [
  {
subject: "math",
score: 10,
  },
  {
subject: "chinese",
score: 20,
  },
  {
subject: "english",
score: 30,
  },
];
varsum=result.reduce(function (prev, cur) {
returnprev+cur.score;
}, 0);
console.log(sum); // 60



5 多维数组转一维

constarr= [
  [1, 2],
  [
    [3, 4, [5, 6, 7], 8],
    [9, 10],
  ],
11,
  [12, 13, [14, 15]],
];
constflttenArr= (arr) => {
returnarr.reduce((prev, cur) => {
returnprev.concat(Array.isArray(cur) ?flttenArr(cur) : cur);
  }, []);
};
flttenArr(arr); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]



6 按顺序运行promise

functionrunPromiseInSequence(arr, input) {
returnarr.reduce((promiseChain, currentFunction) => {
/*     * 1、Promise {<fulfilled>: 10}     * 2、Promise {<fulfilled>: 50}     * 3、Promise {<fulfilled>: 100}     * 4、Promise {<fulfilled>: 300}     */console.log("acc:", promiseChain);
/*     * 1、p1     * 2、p2     * 3、f3     * 4、p4     */console.log("cur:", currentFunction);
returnpromiseChain.then(currentFunction);
  }, Promise.resolve(input));
}
// promise function 1functionp1(a) {
returnnewPromise((resolve, reject) => {
resolve(a*5);
  });
}
// promise function 2functionp2(a) {
returnnewPromise((resolve, reject) => {
resolve(a*2);
  });
}
// function 3  - will be wrapped in a resolved promise by .then()functionf3(a) {
returna*3;
}
// promise function 4functionp4(a) {
returnnewPromise((resolve, reject) => {
resolve(a*4);
  });
}
constpromiseArr= [p1, p2, f3, p4];
runPromiseInSequence(promiseArr, 10).then(console.log); // 1200



7 管道运算

constdouble= (x) =>x+x;
consttriple= (x) =>x*3;
constquardple= (x) =>x*4;
constpipe=  (...functions) =>  (initVal) =>functions.reduce((acc, cur) => {
returncur(acc);
    }, initVal);
constmultiply4=pipe(double, double);
constmultiply6=pipe(double, triple);
constmultiply8=pipe(double, quardple);
constmultiply9=pipe(triple, triple);
constmultiply12=pipe(triple, quardple);
constmultiply16=pipe(quardple, quardple);
constmultiply24=pipe(double, triple, quardple);
multiply4(10);
multiply6(10);
multiply8(10);
multiply9(10);
multiply12(10);
multiply16(10);
multiply24(10);



利用reduce实现map

constarr= [1, 3, 5, 7, 9];
constnewMappedArr=arr.map((item) =>item*item);
console.log(newMappedArr); // [1, 9, 25, 49, 81]// thisArg可选 执行 callback 函数时值被用作this。// 不能使用箭头函数,没有thisArray.prototype.mapByReduce=function (cb, thisArg) {
returnthis.reduce((acc, cur, index, arr) => {
acc[index] =cb.call(thisArg, cur, index, arr);
returnacc;
  }, []);
};
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/mapconstnewMRArr=arr.mapByReduce((item) =>item*3);
console.log(newMRArr);



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.reduceif (!Array.prototype.reducee) {
// 往 Array.prototype 对象上定义reduce属性,可被调用Object.defineProperty(Array.prototype, "reducee", {
// 其值为函数value: function (callback/*, initialValue*/) {
// this 为 reduce 方法的调用者,即某个数组if (this===null) {
thrownewTypeError(
"Array.prototype.reduce "+"called on null or undefined"        );
      }
if (typeofcallback!=="function") {
thrownewTypeError(callback+" is not a function");
      }
// 1. Let O be ? ToObject(this value).// Object 构造函数将给定的值包装为一个新对象。this为null或undefined时,o={}varo=Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).// >>> 按位无符号右移运算符varlen=o.length>>>0; // 保证是个数字,可以将结果undefined变为0// Steps 3, 4, 5, 6, 7vark=0; // 即下标indexvarvalue; // 累加器,每次迭代的终值// 参数长度大于等于2if (arguments.length>=2) {
// 参数列表第一个是回调函数,第二个是 初始值 initialValuevalue=arguments[1];
      } else {
// 正常有值数组,len>0,对象o中不存在k索引 ??? 排除无效的o属性while (k<len&&!(kino)) {
k++;
        }
// 3. If len is 0 and initialValue is not present,//    throw a TypeError exception.// 空数组 & 无初始值if (k>=len) {
thrownewTypeError(
"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 (kino) {
// 记得我们给的callback里,函数最后要return,不然value就拿不到值了value=callback(value, o[k], k, o);
        }
// d. Increase k by 1.k++;
      }
// 9. Return accumulator.returnvalue;
    },
  });
}
目录
相关文章
|
19天前
Array.from() 与 Array.reduce()
Array.from() 与 Array.reduce()
15 1
|
19天前
实现array.slice()方法
实现array.slice()方法
|
7月前
|
存储 测试技术 C++
map + function 实现替代if - else
代码更简洁:使用map + function可以将多个if-else语句转化为一行代码,使得代码看起来更加简洁易懂。 可读性更好:使用map + function可以将判断逻辑抽象成函数,让代码更具可读性和可维护性。
41 0
|
10月前
关于数组中forEach() 、map()、filter()、reduce()、some()、every()的总结
关于数组中forEach() 、map()、filter()、reduce()、some()、every()的总结
30 0
|
10月前
Array.prototype.concat
Array.prototype.concat
39 0
|
10月前
|
前端开发 索引
Array.prototype.at
Array.prototype.at
52 0
|
索引
Array.forEach()
Array.forEach()
64 0
ES6常用数组方法总结(max,contant,some,every,filter,reduce,forEach,map)
1.求最大值(Math.max) 2.数组添加到另外一个数组的尾部(...扩展符) 3.复制数组 3.1数组直接赋值 3.2 es5通过concat方法进行克隆,不会影响原来数组 3.3 es6通过扩展字符串来实现克隆 4.用Object.keys清空对象的属性值 5.forEach,遍历数组,无返回值,不改变原数组 6.map遍历数组,返回新数组,不改变原数组 7.filter,过滤掉数组不符合条件的值,返回新数组,不改变原数组 8.reduce 9 some() 10.every
147 0
ES6常用数组方法总结(max,contant,some,every,filter,reduce,forEach,map)
|
索引
Array.prototype.flatMap()
Array.prototype.flatMap()
65 0
Array.prototype.flat()
Array.prototype.flat()
64 0