将数组还原为树的各种姿势

简介: 将数组还原为树的各种姿势

a1355abfbc23d339cdde402bd5b143e.png

前言

工作中面对将数组还原为树的需求应该说是相当常见,例如功能菜单,部门组织等等,都有类似的功能,需要前端将后端传递过来的扁平数组还原成带有层级关系的组织树,那么具体有哪些实现方法呢,今天就和大家一起来探讨探讨

问题分析

通过 idpid 来关联数据,应该是我们工作中最常用的方案之一,存储模型如下

id pid data
1 0 a
2 1 b
3 1 c

该模型代表了如下的树状结构

{
    id: 1,
    pid: 0,
    data: 'a',
    children: [
        {id: 2, pid: 1, data: 'b'},
        {id: 3, pid: 1, data: 'c'},
    ]
}
复制代码

今天就以这种数据结构为例,来编写我们数组转树的方法

数组转树

const list = [
  { pid: null, id: 1, data: "1" },
  { pid: 1, id: 2, data: "2-1" },
  { pid: 1, id: 3, data: "2-2" },
  { pid: 2, id: 4, data: "3-1" },
  { pid: 3, id: 5, data: "3-2" },
  { pid: 4, id: 6, data: "4-1" },
]
复制代码

递归

讲递归前顺便过一下数组中的 reduce 方法,对处理类似问题的结果时非常好用,可以让我们逐项处理数据时得到一个叠加的结果

reduce() 方法接收一个函数作为累加器,reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素

callback: 函数中主要用到的2个参数

  • previousValue (上一次计算的结果或初始值)
  • currentValue (当前的项)

例如我们简单的把数组的每一项都放在一个 tree 里边

const tree = list.reduce((res, item)=> {
    res.push(item)
    return res
}, [])
console.log(tree);
复制代码

本题使用递归时,需要明确我们递归过程得到返回值是一个数组,而最终结果是一个层级嵌套的数组,终止条件是循环遍历完每一项

function listToTree(list, pid) {
    const node = list.reduce((res, item) => {
        if (item.pid === pid) {
            // 匹配到该项进入下一层递归
            const children = listToTree(list, item.id)
            item.children = children
            // 如果匹配到,返回上次结果 + 匹配项
            return [...res, item]
        }
        // 如果没匹配到,返回上次结果
        return res
    }, [])
    return node
}
console.log(listToTree(list, null))
复制代码

迭代

运用迭代同样需要做循环遍历,只是这里我们会利用 js 对象是一个引用类型的特点,通过直接修改对象的属性,来最终确认层级关系

function listToTree(list, pid) {
    const map = {}
    const tree = []
    list.forEach(item => {
        map[item.id] = item
        item.children = []
    })
    list.forEach(item => {
        if (item.pid === pid) {
            // 只添加根元素,其余通过映射关系关联
            tree.push(item)
        } else {
            // 通过映射关系维护层级关系
            map[item.pid].children.push(item)
        }
    })
    return tree
}
console.log(listToTree(list, null))
复制代码

这里使用了用 map 空间换时间,用于将所有项的 id 及自身记录到字典中,对执行效率进行了优化

相关文章
|
7月前
|
存储 算法
【每日挠头算法题(6)】二叉树的所有路径|神奇字符串
【每日挠头算法题(6)】二叉树的所有路径|神奇字符串
|
16小时前
平衡二叉树的插入和删除(从现在开始摆脱旋转)
平衡二叉树的插入和删除(从现在开始摆脱旋转)
16 9
|
16小时前
|
索引
leetcode代码记录(下一个更大元素 I
leetcode代码记录(下一个更大元素 I
7 0
|
16小时前
leetcode代码记录(下一个更大元素 II
leetcode代码记录(下一个更大元素 II
8 0
|
16小时前
leetcode代码记录(二叉树的最小深度
leetcode代码记录(二叉树的最小深度
9 0
|
12月前
|
Python
Python 再说勾股树,这次整一棵五彩的任意“生长”的分形树!
Python 再说勾股树,这次整一棵五彩的任意“生长”的分形树!
145 0
|
算法
删除链表中间节点(变形题目,简单难度)
删除链表中间节点(变形题目,简单难度)
66 1
删除链表中间节点(变形题目,简单难度)
|
算法 Java
【算法】字符串复原IP地址,从前序与中序编辑序列构造二叉树 三道算法题
字符串复原IP地址,从前序与中序编辑序列构造二叉树
91 1
【算法】字符串复原IP地址,从前序与中序编辑序列构造二叉树 三道算法题
|
算法
【算法竞赛进阶指南】程序自动分析(并查集判冲突+离散化)
【算法竞赛进阶指南】程序自动分析(并查集判冲突+离散化)
95 0
【区间求和の解决方案】307. 区域和检索 - 数组可修改 :「树状数组」&「线段树」
【区间求和の解决方案】307. 区域和检索 - 数组可修改 :「树状数组」&「线段树」