乐趣区

关于javascript:使用reduce方法处理树形结构数据

定义

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

reduce() 与 forEach()、map()、filter()这些办法一样,也会对数组中的每一项进行遍历,然而 reduce() 能够将遍历的前一个数组项产生的后果与以后遍历项进行运算。

语法

array.reduce(function(prev, cur, index, array){...}, init)

回调函数中的参数:

  • prev 必须。示意调用回调时的返回值,或者初始值 init。
  • cur 必须。示意以后元素。
  • index 可选。示意以后元素的索引。
  • array 示意原数组。
  • init 可选。初始值,作为第一次调用回调函数的第一个参数。

其中罕用的参数:prevcur

留神 :回调函数第一次执行时,prev 和 cur 的取值有两种状况:如果调用 reduce() 时提供了初始值 init,prev 取 init 值,cur 取数组中的第一个值,此时索引从 0 开始;如果没有提供初始值 init,则 prev 取数组中的第一个值,cur 取数组中的第二个值,此时索引从 1 开始。

实例

1. 没有传递初始值 init

const arr = [1, 3, 5, 7]  
    arr.reduce(function(prev, cur, index, arr){console.log(prev, cur, index)  
     return prev + cur  
})

每次调用的参数和返回值如下表:

callback prev cur index array return value
第 1 次 1 3 1 [1, 3, 5, 7] 4
第 2 次 4 5 2 [1, 3, 5, 7] 9
第 3 次 9 7 3 [1, 3, 5, 7] 16

因为没有传入初始值,所以索引是从 1 开始,callback 被调用三次,开始时 prev 的值为数组第一项 1,cur 的值为 3,相加之后返回值 4 作为下一轮回调的 prev 值,而后持续下一轮的回调,直至实现后返回。

2. 传递初始值的状况下

const arr = [1, 3, 5, 7]  
arr.reduce(function(prev, cur, index, arr){console.log(prev, cur, index)  
    return prev + cur  
}, 10)

每次调用的参数和返回值如下表:

callback prev cur index array return value
第 1 次 10 1 0 [1, 3, 5, 7] 11
第 2 次 11 3 1 [1, 3, 5, 7] 14
第 3 次 14 5 2 [1, 3, 5, 7] 19
第 4 次 19 7 3 [1, 3, 5, 7] 26

3. 数组去重

const arr = ['ab', 'v', 'd', 'ab', 'h', 'e', 'dc', 'e', 'e', 'f']  
    const newArr = arr.reduce(function(prev, cur){!prev.includes(cur) && prev.push(cur)  
     return prev  
}, [])  
console.log(newArr) // ["ab", "v", "d", "h", "e", "dc", "f"]~~~~

执行的步骤如下:

  • 初始化一个空数组
  • 第一次调用时,prev 为初始值即空数组,cur 为数组中的第一项 arr[1],而后在 prev 中查找 cur 是否曾经存在,如果不存在就将该项增加到 prev 中,并 prev 返回进入下一次回调
  • 第二次回调时,prev 为第一次的返回值,cur 为数组中的第二项 arr[2],而后在 prev 中查找 cur 是否曾经存在,如果不存在就将该项增加到 prev 中,并 prev 返回进入下一次回调
  • … …
  • 最初将 prev 这个数组返回

4. 利用 reduce 对数组中的 Object 对象进行分组及合并

// 从后盾获取的对象数组,依据对象的 type 进行分组合并成 tree 树形展现数据
const dataArr = [{ type: '治理层', name: 'hive_82', reserve: '2', id: 1},
    {type: '原始数据层', name: 'qwe', reserve: '1', id: 2},
    {type: '贴源层', name: 'mysql_exchangis', reserve: '3', id: 3},
    {type: '治理层', name: 'links_188', reserve: '1', id: 4},
    {type: '贴源层', name: 'mysql_ces', reserve: '2', id: 5}
]
const treeData = dataArr.reduce((cur, next) => {const obj = cur.find(curItem => curItem.label === next.type) if (obj) {if (obj.children.indexOf(next.id) === -1) { // 去重解决
 obj.children.push({ 
                ...next, 
                label: next.name 
            })
        }
    } else {
        const newObj = {
            label: next.type,
            children: [{
                ...next,
                label: next.name
            }]
        }
        cur.push(newObj)
    } return cur
}, [])
​ // 合并后的后果:treeData = [
    {
        label: '治理层',
        children: [{ type: '治理层', name: 'hive_82', reserve: '2', id: 1, label: 'hive_82'},
            {type: '治理层', name: 'links_188', reserve: '1', id: 4, label: 'links_188'}
        ]
    },
    {
        label: '原始数据层',
        children: [{ type: '原始数据层', name: 'qwe', reserve: '1', id: 2, label: 'qwe'}
        ]
    },
    {
        label: '贴源层',
        children: [{ type: '贴源层', name: 'mysql_exchangis', reserve: '3', id: 3, label: 'mysql_exchangis'},
            {type: '治理层', name: 'mysql_ces', reserve: '2', id: 5, label: 'mysql_ces'}
        ]
    }
]

5. 利用 reduce 解决菜单后端返回的菜单构造如下,须要依据

办法一:

const dataArr = [{id: '18', name: '重置明码', parentId: '30',parentName: '用户治理'}, 
    {id: '13', name: '审计日志', parentId: '29', parentName: '系统管理'}, 
    {id: '29', name: '系统管理', parentId: '0', parentName: null}, 
    {id: '14', name: '批改', parentId: '33', parentName: '部门治理'}, 
    {id: '2', name: '用户列表', parentId: '30', parentName: '用户治理'}, 
    {id: '30', name: '用户治理', parentId: '29', parentName: '系统管理'}, 
    {id: '33', name: '部门治理', parentId: '0', parentName: null}, 
    {id: '37', name: '增加用户', parentId: '30', parentName: '用户治理'}, 
    {id: '6', name: '增加', parentId: '33', parentName: '部门治理'}, 
    {id: '7',name: '删除', parentId: '33', parentName: '部门治理'}
] // 创立菜单 id 的映射关系
const idMapping = dataArr.reduce((prev, next, i) => {prev[next.id] = i return prev
}, {})
​
const treeData = []
dataArr.map(el => { // 一级菜单
    if (el.parentId === '0') {treeData.push(el) return } // 通过映射找到父元素
    const parentEl = dataArr[idMapping[el.parentId]] 
​ // 把以后元素增加到父元素的 `children` 数组中
    parentEl.children = [...(parentEl.children || []), el]
})
console.log(treeData)

办法二:

// 依据 parentId 创立映射关系
const result = dataArr.reduce((prev, next) => {prev[next.parentId] ? prev[next.parentId].push(next) : prev[next.parentId] = [next]; return prev;
}, {});
      
Object.keys(result).map(key => {result[key].map((item, i) => {result[item.id] ? item.children = result[item.id] : '' });
}) this.treeData = result[0]
console.log(treeData)

还能够通过递归的办法来实现,具体就不赘述了

最初生成的数据结构如下图所示:

![image](/img/bVbMEAT)

退出移动版