非标准树状结构数据的处理

那年仲夏 提交于 2020-02-17 17:44:34

      之前笔者写过两篇博客来讲如何处理标准的树状结构数据,分别是js遍历树形结构方法过滤树形结构数据并获取新的树形结构。这次就来聊一下如何处理非标准树状结构数据。标准的树状结构数据的下级节点存放的字段是统一的比如说我们常见的children,有一些特别的数据为了语义上更加明确,所以每一级的子级所在的字段都不一样,比如存放省市区标识的数据用cityList存放下级市,areaList存放下级的区。下面来讲一下,如何遍历这种非标准树形结构,并且如何将他们转换成标准的树状结构数据。

     先来看一下我们要处理的数据结构,因为内容较多这里就只放出部分截图:

 

     处理树状结构的方法用递归是最适合不过了。这里也顺带讲一下为什么我们不用循环嵌套的方式去遍历树形结构比如每一级的循环都用for,或者是forEach等等。主要的原因是后期难以维护和扩展,比如树状结构再新增一级,要是用for, map, forEach 等遍历,那你就得再写上一层。层级结构会越来越深,而且每一层相关的判断逻辑可能都需要重新复制一遍。如果用递归,那么他的逻辑结构是很稳定的,如果仅仅是新增了一级,递归的逻辑是仍然有效的,代码无需变动。如果需要修改判断逻辑,基本上只要在递归的主逻辑上稍微修改一下就行,数据的封装和向下传递是基本不需要动的。

   处理树状结构数据考验的就是递归的功力。递归主要搞清楚两点,第一点是进入下一次递归的条件,第二点是退出递归的条件。通过数据分析,进入下一次递归的条件就是cityList和areaList里面还有没有没数据,至于退出递归的条件这里选择当前数据为空的时候就行了。下面我们来看具体代码:

function queryCityList (list = [], ret = []) {
    if (!list.length) return []
    for (let area of list ) {
        let item = {
            id: area.id,
            name: area.name
        }
        ret.push(item)
        if (area.cityList || area.areaList) {
            item.children = []
            queryCityList(area.cityList || area.areaList, item.children)
        }
    }
    return ret
}

  temp是我们的原始数据,我们看一下数据的处理结果:

 我们看到queryCityList这个方法已经把我们非标准的树状结构数据变成了标准的树状结构数据并进行了输出。

下面贴一段标准树状结构遍历的方法,其实这个和非标准结构的遍历方法几乎一样,最主要的只不过是更改了进入下一次递归的判断条件而已:

function ergodicTree (tree = [], ret = []) {
    if (!tree.length) return []
    for (let item of tree ) {
        let node = {
            id: item.id,
            name: item.name
        }
        ret.push(node)
        if (item.children && item.children.length) {
            node.children = []
            ergodicTree(item.children, node.children)
        }
    }
    return ret
}

这里的代码还有一种比较精简的写法,如下所示:

function ergodicTree (tree = [], ret = []) {
    if (!tree.length) return []
    for (let item of tree ) {
        let node = {
            id: item.id,
            name: item.name,
            children: []
        }
        ret.push(node)
        if (item.children && item.children.length) ergodicTree(item.children, node.children)
    }
    return ret
}

对比发现,这样其实只是把定义节点children的时机挪了一下位置。这样代码看起来会更紧凑,带来的缺点就是你的最后一级节点会带有一个空的数组,示例如下:

 笔者觉得这两种方式都行,空数组的存在并不影响操作,同时也为每个节点预留了一个下级节点的存储字段,至于怎么选择就看各位了。如有任何疑问,请可于博客下方留言。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!