reduce()方法

213 阅读5分钟

引言

最近复习面试题的时候,看到 reduce() 这个方法的,便借此篇文章梳理一下 reduce()

概念

reduce() 是 JavaScript 中数组的一个内置方法,是一个迭代方法。MDN是这样定义的:

reduce()  方法对数组中的每个元素按序执行一个由您提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。

简而言之,就是 reduce() 方法对数组的每个元素运行一个 reducer 回调函数,得到一个值。

语法

reduce(callbackFn, [initialValue])
reduce((accumulator,currentValue,currentIndex,array)=>{}, [initialValue])

reduce 方法需要两个参数:

  • callbackFn 回调函数 :是数组中每个元素执行的函数,其返回值将作为下一次调用 callbackFn 时的 accumulator 参数,回调函数有四个参数:

    • accumulator :必需值,表示上一次调用 callbackFn 的结果。在第一次调用时,如果指定了 initialValue 初始值,则该值为初始值,否则为 arrary\[0] 的值。

    • currentValue :必需值,表示当前元素的值。在第一次调用时,如果指定了 initialValue 的值,则为 array\[0] 的值,否则为 array\[1]

    • currentIndex :可选值,表示数组中的索引位置。在第一次调用时,如果指定了 initialValue ,则为 0,否则为 1 。

    • array :可选值,表示要处理的数组。

  • initialValue 初始值:可选参数,表示第一次调用回调函数时的初始化值。

例子

let arr = [15, 16, 17, 18, 19]

// 无初始值
let res1 = arr.reduce((pre, cur) => pre + cur);

// 有初始值
let res2 = arr.reduce((pre, cur) => pre + cur,10);

  • 无初始值时,reduce()的回调函数被调用四次,数组索引为 0 的元素将被用作初始值,迭代器将从索引为 1的元素开始执行 。
  • 有初始值时,reduce()的回调函数被调用五次,迭代器从索引为0的元素开始执行。

一些常见的用例

求加法/乘积

let arr = [1,2,3,4,5]  
console.log(arr.reduce((a,b) => a + b))         
console.log(arr.reduce((a,b) => a * b),2)    

// 输出:  15  240

对象中的属性值求和

var arr = [
    {
        name: 'ww',
        age: 19
    },
    {
        name: 'xx',
         age: 20
    },
    {
        name: ' yy',
        age: 21
    }
];

var sum = arr.reduce((pre, cur) => {
    return cur.age + pre;
}, 0);

console.log(sum) 

// 输出: 60

求最大值(最小值)

// 求最大值 
let arr = [23,123,342,12];

let max = arr.reduce((pre, cur) => {
  return pre > cur ? pre : cur
});

console.log('max:',max);

//求最小值
let min = arr.reduce((pre, cur)=>{
    return pre < cur ? pre : cur
})

console.log('min:',min);

// 输出: max: 342   min: 12

数组去重

let arr = [3,4,2,4,3,1,6]
let result = arr.reduce((pre, cur) => {
	if (!pre.includes(cur)) {
		pre.push(cur)
	}
	return pre
}, [])

console.log(result)

// 输出:[3,4,2,1,6]

统计某个元素(字符串、字符、单词等等)在数组中出现的次数

let arr = ["name","age","name","weight","weight","name","age"]

let result = arr.reduce((pre, cur)=> {
  if (cur in pre){
    pre[cur]++     // 若该元素在累加器里面存在,则设置将该元素的属性值+1
  }
  else{
    pre[cur] = 1   // 若该元素在累加器里面没有,则设置该元素的属性值为1 
  }
  return pre
},{})

console.log(result); 

//  输出:{ name: 3, age: 2, weight: 2 }

// let b = {'e' : 1, '3' : 2}
// console.log(b['e']);   获取对象中属性值
// b['e'] = 3             设置对象中的属性值
// console.log(b['e']);

解析: 微信图片_20240314181752.png

多维数组转化为一维(展平数组)

// 二维转一维
let arr = [[0, 1], [2, 3], [4, 5]]    
let newArr = arr.reduce((pre,cur) => {
    return pre.concat(cur)              
},[])

console.log(newArr);  

// 输出: [0, 1, 2, 3, 4, 5]

// 多维转一维
let arr2 = [[0, 1], [2,[3,4,5]], [6, 7]]
const newArr = function(arr){
   var res = arr.reduce(function(pre,cur){
    // console.log('cur',cur);
    return pre.concat(Array.isArray(cur)?newArr(cur):cur)
  },[])
  return res
}

console.log(newArr(arr2)); 

// 输出: [0, 1, 2, 3, 4, 5, 6, 7]

解析: 124ac618fd24b0051c293ead59e4f7e.png

按属性分组对象

let obj = [
  {name: '李四', marks: 41},
  {name: '张三', marks: 59},
  {name: '王五', marks: 36},
  {name: '孙六', marks: 90},
  {name: '苏二', marks: 64},
];

let init = {
  pass: [], 
  fail: []
}

let res = obj.reduce((pre, cur) => {
  (cur.marks >= 50) ? pre.pass.push(cur) : pre.fail.push(cur);
  return pre;
}, init);

console.log(res);

 
// 输出:
// {
//   pass: [
//     { name: '张三', marks: 59 },
//     { name: '孙六', marks: 90 },
//     { name: '苏二', marks: 64 }
//   ],
//   fail: [ { name: '李四', marks: 41 }, { name: '王五', marks: 36 } ]
// } 

更多了解reduce()方法应用

reduce()

优缺点

优点

  • 处理复杂逻辑:当需要对数组中的元素进行复杂的累积或组合操作时,reduce() 通过提供一个自定义的 reducer 函数,可以实现复杂的计算和数据转换。

  • 初始值可定制reduce() 方法接受一个初始值作为可选参数,这使得在处理数组时可以有一个明确的起点,有助于处理一些边界情况。

  • 简洁性:相比于使用传统的循环结构(如 for 循环或 while 循环),reduce() 可以将多个步骤的操作合并为一个,代码量更少,代码更加紧凑。

  • 例如:使用 filter()和 map()会遍历数组两次,但是你可以使用 reduce() 只遍历一次并实现相同的效果,从而更高效。

缺点

  • 复杂性:如果不仔细考虑,reduce()可能会导致代码复杂且难以阅读。

  • 调试:由于reduce()它是一个高阶函数,因此由于其多步骤性质,调试错误可能更具挑战性。

何时避免使用reduce()

  • 代码可读性:虽然reduce()它是一个强大的工具,但它也可能导致复杂的代码。对于简单的数组转换或聚合,使用其他数组方法(例如map() 或 filter())可以使代码更具可读性和可维护性。

  • 性能注意事项:对于需要执行简单的操作(例如查找数组中的最大值或最小值),使用特定方法(例如Math.max()或 Math.min())会比 reduce() 更有效。reduce() 方法需要遍历整个数组,所以对于大型数组或性能敏感的应用,这可能会成为性能瓶颈。

  • 错误处理:与循环结构不同,reduce() 方法不会因 reducer 函数中的错误而中断执行。这可能导致错误被忽略或难以调试。需要确保 reducer 函数能够妥善处理所有可能的错误情况。

最后

总的来说,reduce() 是一个强大的工具,可以利用它的强大功能简化复杂的数组操作,同时保持代码的可读性和效率。但并不总是最佳选择,在选择是否使用它时,应该考虑具体需求。