前端面试题系列之数组与树互转

496 阅读2分钟

数组转为树

// 通常情况下,会给你排序好的数组。但不排除个别情况下考察你是否真正用过,所以会打乱顺序。因此,写出来的代码也要适用于乱序的情况

const arr = [
    {id:1, name: '部门A', parentId: 0},
    {id:2, name: '部门B', parentId: 1},
    {id:3, name: '部门C', parentId: 1},
    {id:4, name: '部门D', parentId: 2},
    {id:5, name: '部门E', parentId: 2},
    {id:6, name: '部门F', parentId: 3},
    {id:7, name: '部门G', parentId: 4},
    {id:8, name: '部门H', parentId: 4}
]

思路:
    1. 遍历数组
    2. 每个元素,都生成tree node
    3. 找到parentNode,并加入到它的children中

问题来了:如何找到parentNode呢
首先,每个元素都有id,那么就可以使用Map来维护id与元素的关系。即有id就可以找到这个元素。然后,每个元素又有parentId,这个parentId等于父元素的id。那么就很简单了,通过parentId就能获取到parentNode了。

思路有了,问题也有了方案去解决,那么代码就跟着思路走就行
function convert(arr) {
    const parentMap = new Map() // 每个元素都会把id与元素一一对应
    let res = null
    
    // 第一次循环,是为了把所有元素都放入到map中,防止传入的数组为乱序
    arr.forEach(item => {
        const {id, name} = item
        const treeNode = {id,name}
        parentMap.set(id, treeNode)
    })
    
    arr.forEach(item => {
        const {id, parentId} = item // 拿到每个元素的id和parentId
        const  parentNode = parentMap.get(parentId) //获取该元素的父元素
        if (parentNode){ // 父元素存在
            if (parentNode.children == null) parentNode.children = [] // 父元素的children不存在,则设置为[]
            parentNode.children.push(parentMap.get(id)) // 存在,则加入到父元素的children中
        }
        
        if (parentId === 0) res = parentMap.get(id) // 根元素
    })
    
    return res
}

输出结果如图,非常简单了

image.png

树转数组

思路:
    1. 使用广度优先遍历DOM树
    2. 每个节点,都生成元素
    3. 找到parentId,并加入到元素中
问题来了:如何找到parentId呢?
使用广度优先遍历,拿到每个节点下的子节点,然后将子节点本身与父节点绑定加入到Map中,这样就能通过节点本身拿到父节点,进而拿到父节点的id。

function convert(tree) {
    const parentMap = new Map() // 子节点与父节点一一对应
    const arr = []
    
    const queue = []
    
    // 根元素入队
    queue.unshift(tree)
    
    while(queue.length > 0) {
        const treeNode = queue.pop() // 出队
        if (treeNode ==  null) break  // 如果不存在,则跳出循环
        
        const {id, name, children = [] } = treeNode
        const parentNode = parentMap.get(treeNode) // 通过节点本身拿到父节点
        const parentId = parentNode?.id || 0 // 判断父节点是否有id
        const item = {id, name, parentId}
        arr.push(item)
        
        children.forEach(child => {
            parentMap.set(child, treeNode) // 映射 子节点与父节点的关系
            queue.unshift(child) // 入队
        })
    }
     return  arr
}

输出结果如图,非常简单了

image.png