语法
arr.reduce(callback,[initialValue])
reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组。
callback (执行数组中每个值的函数,包含四个参数)
1、previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))
2、currentValue (数组中当前被处理的元素)
3、index (当前元素在数组中的索引)
4、array (调用 reduce 的数组)
initialValue (作为第一次调用 callback 的第一个参数。)
详解 initialValue 参数
不设置初始值
var arr = [1, 2, 3, 4];
var sum = arr.reduce(function(prev, cur, index, arr) {
console.log(prev, cur, index);
return prev + cur;
})
console.log(arr, sum);
打印结果
上面的例子index是从1开始的,第一次的prev的值是数组的第一个值。数组长度是4,但是reduce函数循环3次。
设置初始值
var arr = [1, 2, 3, 4];
var sum = arr.reduce(function(prev, cur, index, arr) {
console.log(prev, cur, index);
return prev + cur;
},
0) //注意这里设置了初始值
console.log(arr, sum);
打印结果
这个例子index是从0开始的,第一次的prev的值是我们设置的初始值0,数组长度是4,reduce函数循环4次。
提供初始值通常更安全
数组为空不设置初始值会报错
var arr = [];
var sum = arr.reduce(function(prev, cur, index, arr) {
console.log(prev, cur, index);
return prev + cur;
})
//报错,"TypeError: Reduce of empty array with no initial value"
设置初始值不会报错
var arr = [];
var sum = arr.reduce(function(prev, cur, index, arr) {
console.log(prev, cur, index);
return prev + cur;
}, 0)
console.log(arr, sum); // [] 0"
结论:
如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始。
实际应用
(1) 累加和累积
// 累加
const sum = (...num) => {
return num.reduce((sum, num) => sum + num)
}
console.log(sum(1, 2, 3, 4, 5))
// 累积
const accumulator = (...nums) => {
return nums.reduce((acc, num) => acc * num);
};
(2)求最大值最小值
// 原生api
const array = [-1, 10, 6, 5];
const max = Math.max(...array); // 10
const min = Math.min(...array); // -1
// reduce
const array = [-1, 10, 6, 5];
const max = array.reduce((max, num) => (max > num ? max : num));
const min = array.reduce((min, num) => (min < num ? min : num));
(2)拉平嵌套数组
// ES5 flat
const array = [1, [2, [3, [4, [5]]]]];
// expected output [ 1, 2, 3, 4, 5 ]
const flatArray = array.flat(Infinity); // [1, 2, 3, 4, 5]
// reduce
const flat = (array) => {
return array.reduce(
(acc, it) => acc.concat(Array.isArray(it) ? flat(it) : it), []
);
};
const array = [1, [2, [3, [4, [5]]]]];
const flatArray = flat(array); // [1, 2, 3, 4,
(4) 数组去重
通过includes,解构,解构分成了三种写法。
const array = [1, 2, 1, 2, -1, 10, 11]
const uniqueArray1 = [...new Set(array)]
const uniqueArray2 = array.reduce((acc, it) =>
acc.includes(it) ?
acc :
[...acc, it], [])
let newArr = arr.reduce((pre, cur) => {
if (!pre.includes(cur)) {
return pre.concat(cur)
} else {
return pre
}
}, [])
const arr = [10, 10, 20]
const result = arr.reduce((acc, cur) => {
if (acc.indexOf(cur) === -1) {
acc.push(cur)
}
return acc
}, [])
console.log(result)
(5)数组计数 **
const count = (array) => {
return array.reduce((acc, it) => (acc.set(it, (acc.get(it) || 0) + 1), acc), new Map())
}
const array = [1, 2, 1, 2, -1, 0, '0', 10, '10']
console.log(count(array)) // Map(7) {1 => 2, 2 => 2, -1 => 1, 0 => 1, '0' => 1, …}
(6)计算数组中每个元素出现的次数
let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
let nameNum = names.reduce((pre, cur) => {
if (cur in pre) {
pre[cur]++
} else {
pre[cur] = 1
}
return pre
}, {})
console.log(nameNum); //{Alice: 2, Bob: 1, Tiff: 1, Bruce: 1
(7)对象里属性求和
var result = [{
subject: 'math',
score: 10
}, {
subject: 'chinese',
score: 20
}, {
subject: 'english',
score: 30
}];
var sum = result.reduce(function(prev, cur) {
return cur.score + prev;
}, 0);
console.log(sum) //60
(8)获取对象的多个属性,然后赋给新的对象 **
const obj = {
a: 1,
b: 2,
c: 3,
d: 4,
e: 5
// ...
}
const newObj = {
a: obj.a,
b: obj.b,
c: obj.c,
d: obj.d
// ...
}
reduce
const obj = {
a: 1,
b: 2,
c: 3,
d: 4,
e: 5
}
const getObjectKeys = (obj = {}, keys = []) => {
return Object.keys(obj).reduce((acc, key) => (keys.includes(key) && (acc[key] = obj[key]), acc), {});
}
const newObj = getObjectKeys(obj, ['a', 'b', 'c', 'd'])
console.log(newObj)
(9)反转字符串**
const reverseString = (string) => {
return string.split("").reduceRight((acc, s) => acc + s)
}
const string = 'fatfish'
console.log(reverseString(string)) // hsiftaf
(10)根据指定属性,对对象数组中的对象进行分组。
(11) 结合三点运算符
(12)遍历数组,同时对符合条件的数组元素进行操作。
(13)将promise的链式操作,按照顺序执行
对多个promise链式操作进行同步顺序执行,这就对异步的嵌套执行,提供了reduce的解决方案,除了使用async/await语法外,这个在实际项目中也可以用一下
(14)开启由多个函数组成的管道
(15)用reduce模拟数组的map函数
(16)格式化搜索参数
(17)反序列化搜索参数
let search ='https://www.jb51.net/article/251863.htm?name=fatfish&age=100#/home'
let search2 = 'https://www.66152.com/answer/202207/996655.html?name=fatfish&age=100#/home'
(18)实现flat