如何将父子关系的数据转化为树形结构的数组

这篇文章其实是跟上一篇微信小程序-树形菜单替代方案-路径视图组件相关的一篇文章,因为该组件希望传入的是一种树形结构的数据,然而通常我们从后台获取到的数据只是具有父子关系的普通数组。
形如下方:
[{"id":1,"title":"1"},{"id":2,"pid":1,"title":"2"}]
可能希望得到的数据如下:
[{"id":1,"title":"1","children":[{"id":2,"pid":1,"title":"2"}]}]
这篇文章介绍就是如何用javascript实现这种转换:

思路

原始数据的数组成员均带有父级信息,一个父级成员可以是多个子级成员的父级,但一个子级成员永远只有一个父级。这听上去有点拗口,但细细品味应该还是挺好理解的。那么很显然,子级找父级是相对容易的,因为子级知道父级的信息,而且只有一个父级。但是要做成树形结构的数组,就应该是父级知晓子级的信息,以便从顶级一级一级地访问下去找到某个子级
所以我转化的思路就有点像小蝌蚪找爸爸。为什么不是妈妈?事实上如果你喜欢也可以这么理解,反正我们也不关心它是父是母。每个数据成员找到爸爸后,告诉爸爸:爸爸爸爸,我的的地址是在哪里哪里,从此这个爸爸就知道自己有这个子女,它是住在这个地方的,它就可以随时找到这个子女了。(嗯,这些爸爸心就这么大,别人说是它女儿,它就信的,一点都不怀疑。。)这样子每个父亲都知道自己的子信息了,我们只要有辈分最大的那些爸爸的地址,就可以一级一级地找到任意的子级了。
比如我想问辈分最大的某个爸爸,就可以获知所有子级的地址,拜访某个子级a地址找到该子级,又可以从子级a获知所有子级a的子级们的地址了。这就形成了一个树状的信息结构。

原理

对数组进行遍历,让某个数组都找父级,找到父级后就将其本身存入父级的fatherKey字段里,由于对象都是相对引用,这里就相对于告诉父级子级的地址。也就是在父级内存入了子级的地址。大家可以想象一下,父级有了子级的地址,然后子级的家里(对象)又存入了它对应的子级们的地址,这样子那些本身没有父级的父级(即最顶级的父级)就可以通过地址去到下一级的家里,在这个家里找到地址,再直奔它的孙子家里,一直往下。

完善

根据上面的原理已经可以实现代码了,但是还可以进行部分优化,事实上,这个部分是我实现了代码之后进行的,但为了大家能轻松的理解我的代码,将这个过程移到展示代码之前:
首先,数组中的对象是相对引用,这个给了我们转化极大的便利,但是也正因为这样子的一个原因,当我们修改传入的这个数组时,原数组也会被修改,而这可能不是我们所希望的,因此要在转化之前先将传入的数组进行深度拷贝为一个新的数组里,再对这个新的数组进行操作。
然后,因为要每个数组成员都要和其他成员确认它是否是自己的父亲,因此需要进行二次遍历,这里显然成员是不需要和自己确认的,毕竟不可能自己是自己的父级。
接着,当子级找到父级后就不需要和其他成员确认了,因为它只有一个父级,而确认的目的就是要找到父级,目标达成,所以没有进行下去的必要。
最后,有些参数理应由外部传入,因为这些值不同项目需求可能会有差异,但是为了可以更大程度的简化操作,也为这些值配置了默认值
例如:

  1. 每个成员用来记录父级信息的键名是什么
  2. 标识其自身id的键名是什么
  3. 希望存放子级信息的键名是什么
  4. 根级元素其标识父级信息的键值是什么

代码

function toTree({
  value = [],
  fatherKey = 'pid',
  selfKey = 'id',
  childrenKey = 'children',
  rootValue = undefined
} = {}) {
  // 复制对象
  value = JSON.parse(JSON.stringify(value));
  for (let i = 0; i < value.length; i++) {
    let itemI = value[i];
    if (itemI[fatherKey] === rootValue) {
      continue;
    }
    for (let j = 0; j < value.length; j++) {
      if (i === j) {
        continue;
      }
      let itemJ = value[j];
      if (itemI[fatherKey] === itemJ[selfKey]) {
        if (
          !itemJ[childrenKey] ||
          Object.prototype.toString.call(itemJ[childrenKey]) !==
            '[object Array]'
        ) {
          itemJ[childrenKey] = [];
        }
        itemJ[childrenKey].push(itemI);
        break;
      }
    }
  }
  return value.filter(item => item[fatherKey] === rootValue);
}

本文参考了js将有父子关系的数据转换成树形结构数据中u014613927用户的实现。
参考代码截图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值