树状结构转 Map 结构

1,489 阅读3分钟

想到树状数据, 其实大家都有点怵,原因其实也很简单, 数据的增删改查都很困难,需要一层一层的去遍历。 只要数据的层级和数量稍微大一点, 就很容易导致性能出现问题,让你的页面出现操作的明显卡顿。

那怎么解决这个问题, 毕竟很多场景里面, 都会遇到这样的数据结构。这个时候我们就要请出我们今天的主角 Map。

Map 简介

Map 数据类型是 ES6 新增的一种数据结构。它类似于 Object 结构, 也是键值对的集合, 那么 Map 与 Object 有什么不一样的地方呢?

  1. Map 的键值的类型可以是字符串也可以是其他类型, 包括一些复杂数据类型
  2. Map 是有顺序的, 也就是说, 它的遍历顺序跟它的插入顺序一致。
  3. 友好的 api, set 设置, get 获取, size 获取长度, has 判断是否存在。。。

上面我们简单的介绍了一下 Map 数据结构, 那它跟我们的树状结构有什么关系呢? 下面我们就来说说

拍平

我们知道, 多维的数据(树状结构数据), 每次操作某一项数据, 都得层层遍历。这个时候我们会无比想念一维的数据, 因为只需要遍历一次就行了,对每项数据的操作难度也是肉眼可见的降低。其实我们可不可以做到呢, 答案是显而易见的。这个时候我们就可以把树状结构的数据进行拍平, 转换成一维数据。

image.png

转换

image.png

转换后:

image.png

这里面我们可以看到, 我们顺利的把树状结构的数据转换成了一个一维数据。我们可以看到我们把所有的字节点的key保存在了 childrenKeys 数组中,方面后面直接遍历或者判断,同时我们将每个节点的父节点 key 也保存在了parentKey 中, 这样我们通过一个节点, 就可以找到这个节点的父节点和所有子节点。到这里我们就完成了第一步。

怎么快速获取数据

这个时候, 如果我想要获取 key 为 0_1_1 这一项的数据, 我们怎么做?

当然是遍历了, 数据不多的时候, 一点关系的都没有, 但是如果数据量很大呢, 1000 条数据呢, 我们每次操作前都要去获取数据 都遍历一次, 性能可想而知。

这个时候, 我们又在想, 能不能像对象一样, 可以拿着key直接去取数据, 而不必去遍历整个数组。答案当然是可以的。

我们改下上面的转换代码:

image.png

新增

在指定节点下新增一个节点item

// 假如指定节点的key: parentKey
flatMap.set(key, item)
flatMap[parentKey].childrenKeys.push(key)
获取

item = flatMapt.get(key)

修改

flatMap.set(key, ...) 或者 flatMap[key].name = '修改这个数据'

删除

根据一个key删除一个节点

// 判断是否存在这个节点
if(flatMap.has(key)) {
    flatMap.delete(key)
    flatMap[parentKey].childrenKeys = flatMap[parentKey].childrenKeys.filter(childKey => childKey !== key)
}

如何渲染树结构数据

树结构

树结构的渲染就很简单

// 渲染方法
const renderFunc = (data) => {
    
    return data.map(node => {
        if(node.children && node.children.length) {
        return <div>
                 <div>渲染{node.name}</div>
                 {renderFunc(node.children)}
              </div>
       }
       return <div>渲染{node.name}</div>
    })
}

// 树数据遍历
renderFunc(treeData)

这样的渲染其实就是不停递归进行渲染

Map 结构

// 渲染方法
const renderFunc = (data) => {
    if(!data) {
        return null
    }
    const {childrenKeys} = data
    return <div>
              <div>渲染{node.name}</div>
              {childrenKeys.map(key => {
                return  renderFunc(flatMap.get(key))
              })}
           </div>
    })
}

// 从根节点开始渲染
renderFunc(faltMap.get('0'))

这里面的区别就是拿 key去获取节点展示,从根本上来说,其实渲染这块都是差不多的,都是递归

总结

树状结构转 Map 结构,主要的提升在于操作节点(增 删 改 查)的时候可以减少不必要的遍历,从而提升性能。