这个 reduce 函数,说起来其实也挺简单的,但是要实用起来有时候还是感觉有些困难
这是我自己的一些理解方式,分享给大家看一下,希望能帮助到不理解的小伙伴。直接用代码进行逐步的讲解
可能会比较啰(话)嗦(多),但是你看完了,应该就会用了
急性子可以直接去看 更多例子下的二维数组合并 看完应该也能理解
急性子可以直接去看 更多例子下的二维数组合并 看完应该也能理解
我们先来看看主角 reduce 都有哪些参数 【这段内容相对比较重要】
Array.reduce( Function, initValue )
两个参数都非常的重要(虽然第二个可有可无)
Function: 不用说,肯定是个回调函数。由开发者提供 reduce 来回调
initValue: 第一次回调 Function 时会将 initValue 传入到 Function 的第一个参数中ps: 插入一段内容,我发现有的博主没有说。就是当 initValue 没有传入的时候,Function 的第一个参数,将会是数组 0 所对应的值届时 reduce 也将会少走一次循环
接下来说一下 Function 回调时 reduce 都给了哪些参数
function(pre, cur, index, arr)
pre: 上一次的结果集,这个值通常会使用 initValue 作为第一次的默认参数,不过,我上面也有提到,当 initValue 没有传入参数时这个值将会是 数组 的第一个值
cur: 当前元素,与我们使用 forEach/map 时的 item 一样。都是当前循环到的那个对象
通常我们只会使用上面的两个属性
index: 当前元素的索引
arr: 当前元素所属的数组
看完了上面对 reduce 之后,应该就会对 reduce 有一个模糊的了解了,接下来看看它与平常使用方式区别
//跟其他博主一样,我们也用要给简单的数组开始讲
//需求:我们现在有如下这么一个数组,我们想要获得这个数组的总和
let arr = [1,2,3,4,5];
方式一
传统方式,使用一个循环来循环累加,
下面代码是对传统方式的一种例子
这种方式完全可以实现,并且代码量其实也并不复杂。但是我们既然要学习新的高阶函数,那么我们就不在推荐大家使用这种方式了,接下来请看【二】
ps:大家既然都来学 reduce 了,我就默认大家都会使用 => 箭头函数了,这里不对箭头函数做专门的讲解了
let count = 0;
arr.forEach(item=>count+=item);
console.log(count); //15
这方式一实在是太简单了吧。。。我就不讲了
方式二
使用 reduce 函数
如下代码:
let count = arr.reduce(function(pre, cur){
return pre+cur
});
console.log(count); //15
看到这里,你可能会有疑问,这代码量丝毫不比传统方式少,但是为什么还要用这种方式
如果你有这种疑问,那你可能就对 箭头函数 还不够理解,接下来我就要变形了!上面都说了不讲 箭头函数 了,但是我这里还是想用用 function 因为直接用 箭头函数 会变得比较难以理解,上面的那个代码纯粹是为了给初学者带来更好理解的一种方式。希望大家把这两种方式都牢记,能够在心里进行转换
方式二变形
let count = arr.reduce((pre,cur)=>pre+cur)
二维数组合并
//需求:将如下二维数组进行合并处理
let arr2 = [[1,2,3], [4,5,6], [7,8,9]]
插播一则小广告:我这里有一个我自己封装的多维数组拍平也是使用的 reduce 感兴趣的小伙伴可以来瞅瞅 点我查看 看完下面的代码,相信你也能写出来。好了,继续
let newArray1 = arr2.reduce(function(pre,cur){
return pre.concat(cur);
}, []);
let newArray2 = arr2.reduce((pre,cur)=>pre.concat(cur), []);
console.log(newArray2); //[1,2,3,4,5,6,7,8,9]
讲解一下代码执行原理
reduce 第一次进入循环时,会将我们的第二个参数传入到 pre 中
----此时 pre = [], cur = [1,2,3];
----然后我们将 pre 与 cur 使用 concat 拼接成了一个新的数组,并 return
reduce 第二次进入循环
----此时 pre = [1,2,3], cur = [4,5,6]
----然后我们再次将 pre 与 cur 使用 concat 拼接成了一个新的数组,并 return
reduce 第三次进入循环(由于 arr2 长度也就 3 所以这也是最后一次)
----此时 pre=[1,2,3,4,5,6], cur=[7,8,9]
----然后我们再次将 pre 与 cur 使用 concat 拼接成了一个新的数组,并 return
reduce 退出循环 并将 pre 抛出
我们将会得到最后的结果 [1,2,3,4,5,6,7,8,9]
附文:
我们上面有提到过,当我们的 initValue 不传入时,reduce 会将数组第一位作为初始值,并且我们事先知道我们需求中,二维数组是固定的两层数组 如:表格 那么我们这时就可以省掉 reduce 的第二个 initValue 参数(虽然节约不了太多的性能)
当我们去掉 initValue 参数时,我们得到的结果是这样的
reduce 第一次进入循环
----此时 pre = [1,2,3], cur = [4,5,6]
----然后我们将 pre 与 cur 使用 concat 拼接成了一个新的数组,并 return
reduce 第二次进入循环(我们这里其实循环就不再从 0 开始了,而是从 1 开始,所以这也是最后一次循环)
----此时 pre=[1,2,3,4,5,6], cur=[7,8,9]
----然后我们再次将 pre 与 cur 使用 concat 拼接成了一个新的数组,并 return
reduce 退出循环 并将 pre 抛出
我们将会得到最后的结果 [1,2,3,4,5,6,7,8,9]
五个例子
//需求1,对下面数组进行去重处理
//需求2,找到下面数组中的最大数和最小数
//需求3,得到下面数字每次出现的次数
let arr = [1,2,3,1,2,3,4,5,6];
//需求4,对如下数组,根据 name 进行去重处理
let arr2 = [
{name: '小杜'},
{name: '小杜'},
{name: '李四'},
{name: '王五'},
{name: '王五'},
{name: '小青'},
{name: '小青'},
{name: '桃子'},
{name: '小刘'}
];
//需求5,对如下数组根据姓名首字母分类,得到 arr3_result 例子结果
let arr3 = [
{ name: 'Bob' },
{ name: 'Ben' },
{ name: 'Cole' },
{ name: 'Mary' },
{ name: 'Travis' }
]
let arr3_result = {
B: ['Bob', 'Ben'],
C: ['Cole'],
M: ['Mary'],
T: ['Travis']
}
//需求1,对数组进行去重处理
let arr_result = arr.reduce((pre, cur) => {
!!!pre.includes(cur) && pre.push(cur);
return pre;
}, []);
console.log(arr_result); //[1,2,3,4,5,6]
//需求2,找到数组中的最大数和最小数
console.log("最大数", arr.reduce((pre, cur) => pre > cur ? pre : cur)) //6
console.log("最小数", arr.reduce((pre, cur) => pre < cur ? pre : cur)) //1
//需求3, 得到各数字每次出现的次数
console.log("各出现次数结果", arr.reduce((pre, cur) => {
let val = pre[cur] || 0
pre[cur] = ++val
return pre
}, {}))
//需求4,根据 name 进行去重处理
let arr2_result = arr2.reduce((pre, cur, index) => {
if (!pre.names.includes(cur.name)) {
pre.names.push(cur.name)
pre.unique.push(cur)
}
if(index === arr2.length-1){
return pre.unique //最后一次循环时,将唯一的数组丢出
}else{
return pre
}
}, {unique: [], names: []}); //传入两个对象,unique保存名字唯一的对象, names保存唯一的名字
console.log(arr2_result);
//[{name:'小猴'},{name:'小玉'},{name:'桃子'},{name:'小刘'}]
//需求5,对数组 arr3 进行根据姓名首字母分类,得到 arr3_result 例子结果
let tt = arr.reduce(function (pre, cur) {
let name = cur.name // 2.从当前的对象中取出 name 的值
let letter = name[0] // 3.从 name 中取出首字母
let names = pre[letter] // 4.将 letter 从对象中取出
if(names === undefined) names = [] // 4.1 pre 中是否已经存在 letter 的数组,如果没有,则新建一个数组
names.push(name) // 5.将姓名放到 letter 对应的数组中
pre[letter] = names // 6.重写 pre letter对应的数组
return pre // 7.将 pre 丢出作为下一次循环的默认值
// 1.传递一个空对象给 reduce 作为默认值
}, {})
console.log(tt)
更多例子
下面我们举一些其他的例子来讲解
数组求和与数组求平均值
数组与树形相互转换
实现一个我们自己的 reduce
现在,通过我们对上述代码学习后,应该有一点初步的了解了,下面我们对 reduce 进行深入的研究,实现一个我们自定义的 reduce
// 例子
function Array2(arr) {
this.arr = arr
this.reduce = function (func, arg) {
let index = arg === undefined ? 1 : 0 //获得循环起始位置,当没有传默认值时,从 1 开始循环
let pre = arg || this.arr[index] //当给了默认值,则首次使用默认值,否则使用数组 0 的值作为上一次的值
for (let i = index; i < this.arr.length; i++) {
//获得回调结果,将结果作为最后最后一次值
pre = func(pre, this.arr[i], i, this.arr) // 回调的四个参数:上一次结果,当前值,当前索引,当前数组
}
return pre //将结果抛出
}
}
// 使用
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1, 9, 3, 2, 5, 6, 23, 47, 5, 3, 4, 6, 7, 2, 0, -2, -20, -66]
console.log("去重结果", new Array2(arr)
.reduce((pre, cur) => {
!pre.includes(cur) && pre.push(cur)
return pre
}, [])
)
网友评论