JavaScript将扁平数组转换为无限级递归树的两种实现方式

作者:代码拾荒

时间:2026-10-23

浏览:100

本文介绍在JavaScript中将后台返回的扁平化数组转换为无限级嵌套树形结构的两种常用方法。第一种利用递归函数逐层查找子节点,逻辑直观但需注意空子级处理;第二种通过构建对象映射表优化查找效率,性能更优但需警惕直接修改原数据导致的副作用。文章提供完整代码示例及注意事项,适用于菜单、地区等层级数据的处理场景。

文章目录

需求背景

需求

近期开发中遇到一个场景:以往后端直接返回封装好的树形结构,前端无需额外处理嵌套数据(例如地区级联或菜单树)。但此次后端改为返回扁平化的数组数据,要求前端自行处理转换。面对如下所示的,起初感到有些棘手……


const data = [
  {
    "area_id": 5,
    "name": "广东省",
    "parent_id": 0,
  }, 
  {
    "area_id": 6,
    "name": "广州市",
    "parent_id": 5,
  },
  {
    "area_id": 7,
    "name": "深圳市",
    "parent_id": 5,
  },
  {
    "area_id": 4,
    "name": "北京市",
    "parent_id": 3,
  },
  {
    "area_id": 3,
    "name": "北京",
    "parent_id": 0,
  },
  {
    "area_id": 2,
    "name": "测试子地区",
    "parent_id": 1,
  },
  {
    "area_id": 1,
    "name": "测试地区",
    "parent_id": 0,
  }
]

需求背景

换个角度思考,这恰好是一个锻炼数据处理能力的好机会。动手实践后,总结出以下两种整理方案~

方法一:递归实现

方法一——递归

在如此适合递归处理的场景中,递归自然是首选方案。第一种方法,递归登场!具体实现代码如下:


function toTreeData(data,pid){
 
  function tree(id) {
    let arr = []
    data.filter(item => {
      return item.parent_id === id;
    }).forEach(item => {
      arr.push({
        area_id: item.area_id,
        label: item.name,
        children: tree(item.area_id)
      })
    })
    return arr
  }
  return tree(pid) // 第一级节点的父id,是null或者0,视情况传入
}

代码逻辑清晰,在控制台执行测试后效果如下:

需求背景

运行结果符合预期~ 后端无需再关心数据结构,直接返回扁平数据即可。不过,该方法存在一个细节问题:在组件渲染时,若子级为空,中的children属性未显式返回空数组[]。虽然功能可用,但仍有优化空间。至于如何优化,需要开发者自行思考完善,毕竟要成为成熟的程序员,独立解决这类问题也是必修课。

方法二:对象映射实现

方法二——对象映射

在笔者看来,对象映射技术如同倚天屠龙宝刀,掌握其精髓便如获武林秘籍。但若使用不当,也可能变成一堆废铁,甚至引发不可预知的错误。


function setTreeData(arr) {
  // 删除所有 children,以防止多次调用
  arr.forEach(function (item) {
      delete item.children;
  });
  let map = {}; // 构建map
  arr.forEach(i => {
    map[i.area_id] = i; // 构建以area_id为键 当前数据为值
  });

  let treeData = [];
  arr.forEach(child => {
    const mapItem = map[child.parent_id]; // 判断当前数据的parent_id是否存在map中

    if (mapItem) { // 存在则表示当前数据不是最顶层数据
    
      // 注意: 这里的map中的数据是引用了arr的它的指向还是arr,当mapItem改变时arr也会改变,踩坑点
      (mapItem.children || ( mapItem.children = [] )).push(child); // 这里判断mapItem中是否存在children, 存在则插入当前数据, 不存在则赋值children为[]然后再插入当前数据
    } else { // 不存在则是组顶层数据
      treeData.push(child);
    }
  });

  return treeData;
};

console.log(setTreeData(data)); // 输出整理后的数据

具体实现逻辑与递归结果相似,不再赘述执行过程。相比递归,笔者更倾向于这种方法。但需注意一个易错点:该方法会直接修改原数据数组。笔者曾因此踩坑良久,故在实现时先通过删除children属性对数据进行初始化清理。请务必注意这一点,必要时可自行重复确认三遍。

总结

总结

以上简要介绍了两种将扁平化数据转换为递归树结构的方法。目前主要应用场景包括菜单栏、分类树以及省市等有从属关系的数据结构。掌握这两种方法后,后续遇到类似需求便不再担忧。文章持续更新中……

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章