树形结构扁平化及还原实现方法

679 阅读3分钟

1. 数组转换树形结构(树形结构还原)

源数据:默认数据根据pid排列好

    const list = [
        { id: 1, name: '部门1', pid: 0 },
        { id: 2, name: '部门2', pid: 1 },
        { id: 3, name: '部门3', pid: 1 },
        { id: 4, name: '部门4', pid: 3 },
        { id: 5, name: '部门5', pid: 4 },
    ];

转换后数据


const tree = [
    {
            "id": 1,
            "name": "部门1",
            // "pid": 0,
            "children": [
                    {
                            "id": 2,
                            "name": "部门2",
                            // "pid": 1
                    },
                    {
                            "id": 3,
                            "name": "部门3",
                            // "pid": 1,
                            "children": [
                                    {
                                            "id": 4,
                                            "name": "部门4",
                                            // "pid": 3,
                                            "children": [
                                                    {
                                                            "id": 5,
                                                            "name": "部门5",
                                                            // "pid": 4
                                                    }
                                            ]
                                    }
                            ]
                    }
            ]
    }
]

1.1 递归遍历

const listToTree = (data, parentId = 0) => {
        // 收集结果
        const result = []
        // 递归遍历
        data.forEach(item => {
                // 找到相同的pid进行下一层遍历
                if (item.pid === parentId) {
                        const children = listToTree(data, item.id)
                        if (children.length) {
                                item.children = children
                        }
                        result.push(item)
                }
        });
        return result
}

1.2 通过Map对象实现

循环遍历原始数组,使用对象存储每个节点,并将子节点添加到父节点的children数组中。

function listToTree(data) {
  const map = new Map(); // 用于存储每个节点的引用
  const result = [];

  // 将每个节点存入Map对象
  data.forEach(item => {
    map.set(item.id, Object.assign(item, { children: [] }));
  });

  // 遍历每个节点,将其挂载到其父节点的children属性下
  map.forEach(item => {                                                                        
    if (item.pid === 0) {
      result.push(item);
    } else {
      const parent = map.get(item.pid);
      parent.children.push(item);
    }
  });

  return result;
}

初始化存储的节点

image.png

遍历后的数据

image.png

1.3 对象一次遍历

直接在2的基础上,不需要做两次遍历,

function listToTree2(data){
        // 创建对象 map 和数组 result,分别用来存储所有节点和根节点
        const map = {}
        const result = []

        data.forEach(item=>{
                //遍历数据,并将每个节点保存到 map 对象中
                map[item.id] = item
                if(item.pid===0){
                        result.push(item)
                }else{
                        const parent = map[item.pid]
                        if(!parent.children){
                                parent.children = []
                        }
                        parent.children.push(item)
                }
        })

        return result
}

2. 树形结构扁平化

深度优先和广度优先是两种常见的遍历算法。

  • 深度优先遍历是优先探索深度,直到无法再深入为止
  • 广度优先遍历是优先探索广度,即从离起始节点最近的节点开始扩展。

2.1 深度优先遍历(Depth First Search, DFS)

基本思路:

  1. 创建一个空数组result用来存储扁平化后的结果。
  2. 编写一个dfs函数,函数接收两个参数node和parentId,分别表示当前遍历到的节点和其父节点的id。
  3. 在dfs函数内,首先将当前节点的信息{id, name, pid} push到result数组中。
  4. 然后判断当前节点是否存在子节点,若存在,则递归调用dfs方法,将子节点和当前节点的id作为参数传入。在递归调用前需判断当前节点是否有pid,若无则默认为0。
  5. 最后返回result数组即可。
    /** 深度优先 */
    function flattenTree(tree) {
            const result = []
            function dfs(node, parentId) {
                    result.push({
                            id: node.id,
                            name: node.name,
                            pid: parentId || 0
                    })
                    if (node.children && node.children.length) {
                            node.children.forEach(child => dfs(child, node.id))
                    }
            }
            tree.forEach(node => dfs(node))
            return result
    }

2.2 广度优先遍历(Breadth First Search, BFS)

基本思路:

  1. 创建一个空数组result用于存储扁平化后的结果。
  2. 将树的根节点放入一个队列中
  3. 遍历队列中的节点,分别将节点信息{id, name, pid} push到结果数组中。同时,若当前节点存在子节点,则将子节点依次放入队列末尾。
  4. 当队列为空时,即可结束循环遍历,返回结果数组。
/** 广度优先 */

function flattenTreeBFS(tree) {
        const result = []
        const queue = []

        tree.forEach(node => queue.push(node))
        while (queue.length) {
                const node = queue.shift()
                result.push({
                        id: node.id,
                        name: node.name,
                        pid: node.parentId || 0
                })
                if (node.children && node.children.length) {
                    node.children.forEach(child => queue.push({ ...child, parentId: node.id }))
                }
        }
        return result
}