资讯专栏INFORMATION COLUMN

关于树形插件展示中数据结构转换的算法

王晗 / 1603人阅读

摘要:举例说明如下二维数据结构总部二级门店三级门店二级门店树状数据结构总部二级门店三级门店二级门店但在某些插件中,或在某些特殊场景中,我们有两种数据结构之间相互转换的需求,需要自己写一个辅助函数来完成。

问题背景

在一些目录结构、机构层级等展示的场景中,我们经常会用到一些成熟的树形插件来进行轻松展示,比如ztree等。大多数插件会支持对两种数据源格式的解析,一种是通用的二维数据结构,一种是树状数据结构。对于这两种数据结构的称呼在各插件中可能不尽相同,这里依照二维结构和树状结构来称呼。举例说明如下:

// 二维数据结构
[{
  "id": "001",
  "name": "总部",
  "parentId": "0"
}, {
  "id": "002",
  "name": "二级门店1",
  "parentId": "001"
}, {
  "id": "003",
  "name": "三级门店",
  "parentId": "002"
}, {
  "id": "004",
  "name": "二级门店2",
  "parentId": "001"
}]

// 树状数据结构
[{
    "id": "001",
    "name": "总部",
    "parentId": "0",
    "children": [{
      "id": "002",
      "name": "二级门店1",
      "parentId": "001",
      "children": [{
        "id": "003",
        "name": "三级门店",
        "parentId": "002",
        "children": []
      }]
    }, {
      "id": "004",
      "name": "二级门店2",
      "parentId": "001",
      "children": []
    }]
}]

但在某些插件中,或在某些特殊场景中,我们有两种数据结构之间相互转换的需求,需要自己写一个辅助函数来完成。这里就提供两个这样的工具函数来完成数据结构的转换。

Note: 要说明的是,工具函数没有经过大数据量转换测试,所以对有实时性、大量源数据转换需求的同学而言,请自行测试分析,可采取前置或异步等方案处理。由于自身技术水平的局限性,算法本身会有性能优化的空间,若有更优处理算法,还望交流分享,谢谢!

解决方案

我们来分开介绍两种数据结构之间的转换算法,每个小结中我会先贴出整个函数的代码清单,以便大家复制粘贴,然后会简要说明其中大概的逻辑思路。

二维数据结构 => 树状数据结构
/**
 * 将通用的二维数据结构转换为树状数据结构
 * @param  {String} rootParentIdValue 表示根节点的父类id值
 * @param  {String} parentIdName      表示父类id的节点名称
 * @param  {String} nodeIdName        表示二维结构中,每个对象主键的名称
 * @param  {Array} listData           为二维结构的数据
 * @return {Array}                    转换后的tree结构数据
 */
function listToTree(rootParentIdValue, parentIdName, nodeIdName, listData) {
  if (listData instanceof Array && listData.length > 0 && listData[0][parentIdName]) {
    var rootList = [],
        nodeList = []
      
    listData.forEach(function(node, index) {
      if (node[parentIdName] == rootParentIdValue) {
        rootList.push(node);
      } else {
        nodeList.push(node);
      }
    });

    if (nodeList.length > 0 && rootList.length > 0) {
      childrenNodeAdd(rootList, nodeList);
      return rootList;
    } else if (rootList.length > 0) {
      throw new Error("没有对应的子节点集合");
    } else {
      throw new Error("没有对应的父类节点集合");
    }

    function childrenNodeAdd(rootNodeList, childrenList) {
      if (childrenList.length > 0) { 
        rootNodeList.forEach(function(rootNode) {
          rootNode["children"] = [];
          var childrenNodeList = childrenList.slice(0); 
          childrenList.forEach(function(childrenNode, childrenIndex) {
            if (parentIdName in childrenNode && rootNode[nodeIdName] == childrenNode[parentIdName]) {
              rootNode["children"].push(childrenNode);
              childrenNodeList.splice(childrenIndex, 1);
            }
          });
          childrenNodeAdd(rootNode["children"], childrenNodeList);
        });
      }
    }
    
  } else {
    throw new Error("格式不正确,无法转换");
  }
}

此函数可通过listToTree("0", "parentId", "id", sourceData)调用测试,sourceData为文章开头给出的二维数据结构举例。

下面简要介绍一下其中逻辑,第10行是简要验证一下入参数据的合法性,然后声明了rootList和nodeList两个变量。其中rootList为顶级根节点,nodeList为其他子节点集合,第14行到20行的循环便是为两个变量赋值,之后根据两个变量的值进一步判断数据的合法性。在验证之后调用childrenNodeAdd这个内部函数,此函数之后将会被递归调用,为每一个节点添加指定名称为“children”的子节点数组。两个入参分别是rootNodeList和childrenList,代表父节点集合,和其之后的所有子节点集合。在23行第一次调用时,传入的便是顶级根节点和其之后的所有子孙节点。下面看这段带有详细注解的代码片段:

function childrenNodeAdd(rootNodeList, childrenList) {
      if (childrenList.length > 0) { //  如果没有子节点了就结束递归
        //遍历父节点集合,在子节点中查找其自身的子节点,并添加到对应的子节点数组中
        rootNodeList.forEach(function(rootNode) {
          rootNode["children"] = [];
          var childrenNodeList = childrenList.slice(0); //复制一个子节点数据,用于存放剩余的子节点
          //遍历所有子节点
          childrenList.forEach(function(childrenNode, childrenIndex) {
            if (parentIdName in childrenNode && rootNode[nodeIdName] == childrenNode[parentIdName]) { //根节点的id 等于子节点的父类id
              rootNode["children"].push(childrenNode); //添加对应节点归为子节点
              childrenNodeList.splice(childrenIndex, 1);//在剩余子节点中剔除已经分配过的子节点
            }
          });
          childrenNodeAdd(rootNode["children"], childrenNodeList); //剩余子节点继续递归执行,每次递归一次就表示节点增加一级。
        });
      }
    }
树状数据结构 => 二维数据结构
/**
 * 将树状数据结构转换为二维数据结构
 * @param  {String} childrenName 树状结构中子节点名称
 * @param  {Array} treeData     树状结构数据
 * @return {Array}              转换后的通用二维结构数据
 */
function treeToList(childrenName, treeData) {
  var listData = [];
  transferTreeData(treeData);
  function transferTreeData (sourceData) {
     sourceData.forEach( function(node, nodeIndex) {
     if(node[childrenName].length > 0)
          transferTreeData(node[childrenName]);
      delete node[childrenName];
      listData.push(node);
     });
  }
  return listData;
}

此函数可通过treeToList("children", sourceData)调用测试,sourceData为文章开头给出的树状数据结构举例。这里的逻辑比较简单就不再赘述了。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90515.html

相关文章

  • 用JavaScript自己写MVVM前端框架-VM实现篇

    摘要:关于前端框架大家都有了解,或多或少的使用过,比如,,等等。那么你是否也想自己手写一个的前端框架呢,我们从入手,手把手教你写基于的前端框架,在整个编写的过程中,希望大家学习更多,理解更多。本节我们以打包工具结合转换插件实现数据的抽象。 关于MVVM前端框架大家都有了解,或多或少的使用过,比如Angular,React,VUE等等。那么你是否也想自己手写一个MVVM的前端框架呢,我们从Vi...

    VincentFF 评论0 收藏0
  • 前端数据结构算法(1)-- dfs

    摘要:前端在开发过程中接触到的算法最多的莫过于排序和深度优先遍历。关于算法的遍历过程,我简略的画了一个示例图实例最近在实际业务场景中,跟后端约定页面中所有组件的消息根据页面上的组件聚合到一个对象中,后端返回的是类似如下的一个树形数据结构。 showImg(https://segmentfault.com/img/remote/1460000010632752); dfs 前端在开发过程中接触...

    hizengzeng 评论0 收藏0
  • [译]148个资源让你成为CSS专家

    摘要:层叠样式表二修订版这是对作出的官方说明。速查表两份表来自一份关于基础特性,一份关于布局。核心第一篇一份来自的基础参考指南简写速查表简写形式参考书使用层叠样式表基础指南,包含使用的好处介绍个方法快速写成高质量的写出高效的一些提示。 迄今为止,我已经收集了100多个精通CSS的资源,它们能让你更好地掌握CSS技巧,使你的布局设计脱颖而出。 CSS3 资源 20个学习CSS3的有用资源 C...

    impig33 评论0 收藏0
  • react diff算法

    摘要:算法的本质是对传统遍历算法的优化策略用三大策略将复杂度转化为复杂度策略一中节点跨层级的移动操作特别少,可以忽略不计。当节点处于同一层级时,提供三种节点操作删除插入移动。在旧的节点中的,它的,不满足的条件,因此不做移动操作。 一、react diff算法 diff算法的作用 计算出Virtual DOM中真正变化的部分,并只针对该部分进行原生DOM操作,而非重新渲染整个页面。 传统di...

    imccl 评论0 收藏0
  • SICP Python 描述 3.2 函数和所生成过程

    摘要:函数和所生成的过程来源译者飞龙协议函数是计算过程的局部演化模式。在这一章中,我们会检测一些用于简单函数所生成过程的通用模型。也就是说,递归函数的执行过程可能需要再次调用这个函数。 3.2 函数和所生成的过程 来源:3.2 Functions and the Processes They Generate 译者:飞龙 协议:CC BY-NC-SA 4.0 函数是计算过程的局部演化...

    lolomaco 评论0 收藏0

发表评论

0条评论

王晗

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<