Typescript 前端以及java后端构造组装树型结构数据方法

对于一个树形数据的要点是

  1. 主键id
  2. 父节点id
  3. 子节点集合children[]

也就是自己的唯一标识符,父节点的唯一标识符,以及子节点信息,承上启下,加自己。当然还有扩展信息,比如深度,权重等。

前端解决方案

思路

假设原始数据为这种select  * from tree_table_name类似的查询结果集。即只有单独节点数据信息的数组,不含有子节点集合children[](后端经常返回原始数据不是一两天了)。我们需要通过递归的方式遍历所有节点信息来组装树形数据。这次解决方法对于递归方法参数已经有两个为必定有的:待处理的数据,已处理的数据

对于递归的要点

  1. 参数:待处理数据,处理后数据结果集,传递参数。
  2. 返回值
  3. 递归完成条件:避免死循环,注意何时跳出。

以下先放代码后面再讲讲思路

前端代码

export class TreeConfig{
  id: string = 'id';
  parentId: string = 'pid';
  children: string = 'children';
} 
export function handleTree(data: Array<any>, cfg?: TreeConfig) {
  let res:Array<any> = [];
  if(cfg===undefined){
    cfg = new TreeConfig();
  }
  loadTree(data, res,cfg,undefined);
  return res;
}
function loadTree(data: Array<any>, res:Array<any>, cfg: TreeConfig, cur: any){
  for (let index = 0; index < data.length; index++) {
    const element = data[index];
    if(cur === undefined ){
      if(element[cfg.parentId] === null || element[cfg.parentId] === ""){//寻找根节点
        res.push(element)
        data.splice(index,1);
        index--;
        loadTree(data,res,cfg,element);
      }
    }else if(element[cfg.parentId] === cur[cfg.id]){//处理非根节点
      if(cur[cfg.children] === undefined){
        cur[cfg.children] = [];
      }
      cur[cfg.children].push(element);
      data.splice(index,1);
      index--;
      loadTree(data,res,cfg,element);
    }  
  }
}

先定义TreeConfig是因为我们实际开发中常常遇到的树形数据中的key值可能千奇百怪,但是意思一样,所以给树形结构的数据的key做一个映射。(好比子节点集合不一定就是叫children,还有可能是subs,trees等等)。递归装载数据方法为loadTree,其中参数data为待处理的原始数据(会剔除处理后数据,如果想保留原始数据记得调用前先深度克隆一份),res为处理后的数据,cfg为树形结构的数据的key映射参数,cur为当前处理数据的父节点数据,由于存在根节点,那么对于根节点开始,则cur暂为空undefined。那么这时候就分为两种情况,情况一:当cur为undefined与情况二:cur不为undefined(为一个确切的父节点数据)。

对于情况一:我们需要筛选出根节点数据。原始数据中,成员的父节点的唯一标识为空的为根节点数据(根据实际业务来判断,可能有的业务中根节点的parentId为某个其他值)。将数据存待处理数据源中剔除,并存入res中。然后去递归调用(查找当前节点下的子节点,若没有则不继续递归,若有则会在情况二下继续查找)。index--;的原因是因为这个数据处理后就从待处理就剔除了,需要排除掉。

对于情况二:我们需要找到这个当前父节点下的子节点。即当前处理的数据的parentId等于cur的id。然后将找到的子节点放到他们的父节点的children中,然后继续剔除数据。继续递归调用找这个节点的子节点(如果子节点仍然包含子节点则会情况二继续调用,直到不包含子节点为止,然后执行完函数体返回,即不存在死循环情况)。

java后端解决方案

按照前面思路后端也是可以写类似递归方法的,但是不用这么麻烦,对于经常有各种成熟解决方案的后端,有更好用的工具,而不是去重复造轮子。推荐使用hutool这个工具包(这个工具包包含很多实用工具,其中分页插件也很不错)。maven引入

<dependency>
        <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        <version>5.8.0</version>
</dependency>

后端代码

List<TestModel> list = testDao.queryList(testModel); 
//build 第二个参数null是我这里的数据是所有根节点的parentId为null
return TreeUtil.build(list, null,(treeNode, tree) -> {
			//这里的 treeNode 为list中成员(此时为TestModel),tree为返回list中成员
            tree.setId(treeNode.getId());
            tree.setParentId(treeNode.getPid());
            //以上 tree 的id parentId为必设,不然树形数据组装不了
            tree.setName(treeNode.getName());
            //设置 tree 的扩展属性
            tree.putExtra("description", treeNode.getDescription());
            tree.putExtra("icon", treeNode.getIcon());
		});

当然这个TreeUtil中重载了很多个build方法。这只是其中一个。在这个build方法第三个参数中我们可以灵活操作数据:一个是可以灵活控制树的属性,以及值。比如查询数据需要计算到一个key作为树的属性等,亦或是根据字典转换等等。一个是可以灵活映射id,parentId等。这个 build 方法返回是List<Tree<E>>l类型,即都是树对象Tree的集合,Tree中对于主键来说这里必定是id,对于父节点主键必定为parentId,万一有时候数据返回前端需要parentId为pid,或者其他呢。怎么办?只有在tree的扩展属性中再设置一下例如 tree.putExtra("pid", treeNode.getPid());

吐槽

对于数据处理数据无论是后端还是前端都可以做的时候,经常出现前端后端甩来甩去等现象。可能就几行代码的问题就是推来推去。有时候人与人的想处,比搞几个LeetCode困难算法题还难。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值