reduce 一网打尽

190

作用:

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

语法:

reduce(callback, initValue)

参数:

  • callback:(accumulator,currentValue,index, array)
  • initValue:初始值

返回值: 函数累计处理的结果

相对于forEach,其实reduce仅仅多了个initValue,我们用例子来证明

例子1:求和

求下面数组中isTrue为true的总价格

let arr = [
    { price: 100, brand: '凤凰',num: 2, isTrue: true },
    { price: 1000, brand: '捷安特', num: 3, isTrue: false },
    { price: 500, brand: '迪卡侬', num: 1, isTrue: true }
]

我看先看看用forEach怎么做

let total = 0
arr.forEach(v => {
    if (v.isTrue) {
        total += v.price * v.num
    }
})
console.log(total)

再看看用reduce

let total = arr.reduce((acc, pre) => {
    if (pre.isTrue) {
        return acc += pre.num * pre.price
    } else {
        return acc
    }
}, 0)
console.log(total)

简写为

console.log(arr.reduce((acc, pre) => acc + (pre.isTrue ? pre.num * pre.price : 0), 0))

看到这个例子大家可以看到reduce其实就是比forEach少声明了一个total,其实这个例子reduce返回的就是total

注意: reduce的第二个参数如果不写就默认是数组的第一项

例子2:计算数组中每个元素出现的次数

let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
​
const nameNum = names.reduce((acc, pre) => {
    if (pre in acc) {
        acc[pre]++
    } else {
        acc[pre] = 1
    }
    return acc
}, {})
console.log(nameNum)

例子3:数组去重

let a1 = [1, 2, 3, 4, 4, 1]
​
let newArr = a1.reduce((acc, pre) => {
    if (acc.includes(pre)) {
        return acc
    } else {
        return acc.concat(pre)
    }
}, [])
console.log(3, newArr)
// 简写
a1.reduce((acc,pre) => acc.concat(acc.includes(pre) ? [] : pre), [])

例子4:将二维数组转化为一维

let a2 = [[0, 1], [2, 3], [4, 5]]
​
let singleArr = a2.reduce((acc, pre) => acc.concat(pre), [])
console.log(4, singleArr)

例子5: 将多维数组转化为一维数组

let a3 = [[0, 1], [2, 3], [4, [5, 6, 7]]]
​
function transformArr(arr) {
    return arr.reduce((acc, pre) => {
        if (Array.isArray(pre)) {
            return acc.concat(transformArr(pre))
        } else {
            return acc.concat(pre)
        }
    }, [])
}
console.log(transformArr(a3))

例子6:多维转一维数组(内含对象)

const arr = [
    { id: 1, p_id: 0, name: '首页',
      children: [
        { id: 4, p_id: 1, name: '权限管理',
          children: [
            { id: 6, p_id: 4, name: '角色列表',
              children: [
                { id: 5, p_id: 6, name: '管理员列表',
                },
              ],
            },
          ],
        },
      ],
    },
    { id: 2, p_id: 0, name: '菜单管理' },
    { id: 3, p_id: 0, name: '菜单列表' }
]
​
// function newArrFn(arr) {
//   return arr.reduce((acc, cur) => {
//     if (Array.isArray(cur.children)) {
//       // acc = acc.concat(cur)
//       // return acc.concat(newArrFn(cur.children))
//       return acc.concat(cur, newArrFn(cur.children))
//     } else {
//       return acc.concat(cur)
//     }
//   }, [])
// }// function newArrFn(arr) {
//     return arr.reduce((acc, cur) => acc.concat(cur, Array.isArray(cur.children) ? // newArrFn(cur.children) : []), [])
// }
// 得到的新数组的对象中含有children属性,需要删除
function newArrFn(arr) {
  return arr.reduce((acc, cur) => {
    if (Array.isArray(cur.children)) {
      const copyCur = { ...cur }
      delete copyCur.children
      return acc.concat(copyCur, newArrFn(cur.children))
    } else {
      return acc.concat(cur)
    }
  }, [])
}
console.log(newArrFn(arr))

例子7:一维数组转树形结构

let arr = [
  { id: 1, p_id: 0, name: '首页' },
  { id: 2, p_id: 0, name: '菜单管理' },
  { id: 3, p_id: 0, name: '菜单列表' },
  { id: 4, p_id: 1, name: '权限管理' },
  { id: 5, p_id: 6, name: '管理员列表' },
  { id: 6, p_id: 4, name: '角色列表' }
]
​
// 思路: 
// 1、从数组arr中取出 p_id = 0 的数据 arr_pid0 存到数组中
// 2、然后找他的children,怎么找children呢?
// 3、假设arr_pid0 的 id 为 1,那么我要从arr中取出p_id = 1的数据赋值给children就可以了
// 4、然而咱们的函数就是从arr中取出 p_id === rootValue 的值
// 5、所以const children = newArrFn(arr, cur.id)
// 6、cur.children = children
​
function newArrFn(arr, rootValue = 0) {
    return arr.reduce((acc, cur) => {
      if (cur.p_id === rootValue) {
        const children = newArrFn(arr, cur.id)
        if (children.length) {
            cur.children = children
        }
        acc.push(cur) // 返回值 新数组的长度
      }
      return acc
    }, [])
}
console.log(newArrFn(arr))