如何用JavaScript递归将扁平数据转换为JSON树形结构

来源:个人站长网作者:美园和花头衔:网络博主
导读:本期聚焦于小伙伴创作的《如何用JavaScript递归将扁平数据转换为JSON树形结构》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何用JavaScript递归将扁平数据转换为JSON树形结构》有用,将其分享出去将是对创作者最好的鼓励。

在前端业务开发中,后端接口常返回扁平的数组数据,比如菜单项、部门列表等,而前端渲染时需要层级化的树形JSON结构,递归是实现这种转换的高效方式。递归的核心是通过不断调用自身函数,逐层查找父子节点的关联关系,最终拼接出完整的树形结构。

如何用JavaScript递归将扁平数据转换为JSON树形结构

前置数据结构准备

首先我们需要明确扁平数据和目标树形数据的结构,假设后端返回的扁平数据包含以下字段:

  • id:当前节点的唯一标识
  • parentId:当前节点的父节点标识,顶级节点的parentId为null或者0
  • name:节点展示名称
  • 其他自定义扩展字段

扁平数据示例如下:

// 扁平数组数据
const flatList = [
  { id: 1, parentId: null, name: '总部' },
  { id: 2, parentId: 1, name: '技术部' },
  { id: 3, parentId: 1, name: '产品部' },
  { id: 4, parentId: 2, name: '前端组' },
  { id: 5, parentId: 2, name: '后端组' },
  { id: 6, parentId: 3, name: '需求组' }
];

我们的目标是将其转换为如下树形结构:

[
  {
    "id": 1,
    "parentId": null,
    "name": "总部",
    "children": [
      {
        "id": 2,
        "parentId": 1,
        "name": "技术部",
        "children": [
          { "id": 4, "parentId": 2, "name": "前端组", "children": [] },
          { "id": 5, "parentId": 2, "name": "后端组", "children": [] }
        ]
      },
      {
        "id": 3,
        "parentId": 1,
        "name": "产品部",
        "children": [
          { "id": 6, "parentId": 3, "name": "需求组", "children": [] }
        ]
      }
    ]
  }
]

递归转换核心思路

递归构建树形结构的核心逻辑可以分为三步:

  1. 首先筛选出所有顶级节点,也就是parentId为null或者指定顶级标识的节点
  2. 为每个节点添加children属性,初始值为空数组
  3. 对每个节点递归查找其所有子节点,将子节点放入当前节点的children数组中

递归函数实现

我们首先实现一个递归查找子节点的函数,函数的入参是当前父节点和完整的扁平数据列表:

/**
 * 递归查找当前节点的所有子节点
 * @param {Object} parentNode 当前父节点
 * @param {Array} flatData 完整的扁平数据列表
 * @returns {Array} 当前节点的子节点数组
 */
function findChildren(parentNode, flatData) {
  // 筛选出所有parentId等于当前节点id的子节点
  const children = flatData.filter(item => item.parentId === parentNode.id);
  // 遍历每个子节点,递归查找其子节点
  children.forEach(child => {
    // 为子节点添加children属性
    child.children = findChildren(child, flatData);
  });
  return children;
}

接下来我们实现主转换函数,先筛选顶级节点,再为每个顶级节点调用递归函数:

/**
 * 将扁平数据转换为树形结构
 * @param {Array} flatData 扁平数据数组
 * @param {*} topParentId 顶级节点的parentId值,默认null
 * @returns {Array} 树形结构数组
 */
function flatToTree(flatData, topParentId = null) {
  // 筛选所有顶级节点
  const topNodes = flatData.filter(item => item.parentId === topParentId);
  // 为每个顶级节点递归查找子节点
  topNodes.forEach(node => {
    node.children = findChildren(node, flatData);
  });
  return topNodes;
}

完整调用示例

我们使用前面的扁平数据调用转换函数,查看输出结果:

// 调用转换函数
const treeData = flatToTree(flatList);
// 打印转换后的树形数据
console.log(JSON.stringify(treeData, null, 2));

执行上述代码后,控制台会输出我们预期的目标树形JSON结构,说明转换逻辑正确。

边界情况处理

在实际开发中,可能会遇到一些特殊情况需要处理:

  • 如果扁平数据中存在无效的parentId,也就是找不到对应的父节点,可以选择忽略这些节点,或者将其归为顶级节点,根据实际业务需求调整筛选逻辑
  • 如果数据量较大,递归深度过深可能会导致栈溢出,这时候可以考虑使用循环+映射表的方式替代递归,提升性能
  • 如果原始数据中没有children字段,需要在转换时统一添加,避免后续渲染时出现undefined错误

优化方案:使用映射表提升性能

前面的递归实现需要多次遍历扁平数组,数据量较大时性能较差,我们可以通过构建id到节点的映射表,减少遍历次数:

/**
 * 使用映射表优化的树形转换函数
 * @param {Array} flatData 扁平数据数组
 * @param {*} topParentId 顶级节点的parentId值,默认null
 * @returns {Array} 树形结构数组
 */
function flatToTreeOptimized(flatData, topParentId = null) {
  // 构建id到节点的映射表
  const nodeMap = {};
  flatData.forEach(item => {
    nodeMap[item.id] = { ...item, children: [] };
  });
  const tree = [];
  flatData.forEach(item => {
    const node = nodeMap[item.id];
    if (item.parentId === topParentId) {
      // 顶级节点直接放入结果数组
      tree.push(node);
    } else {
      // 非顶级节点找到父节点,放入父节点的children中
      const parent = nodeMap[item.parentId];
      if (parent) {
        parent.children.push(node);
      }
    }
  });
  return tree;
}

这种方式只需要遍历两次扁平数据,时间复杂度为O(n),比递归遍历的性能更好,适合处理大数据量的场景。

总结

递归是处理树形结构转换的直观方式,核心是通过父子节点标识的关联,逐层拼接节点关系。对于小数据量场景,递归实现简单易懂;对于大数据量场景,可以使用映射表优化的方式提升性能。开发者可以根据实际业务需求选择合适的实现方案,同时注意处理无效数据和递归深度等边界情况,保证代码的健壮性。

JavaScript递归JSON树形结构扁平数据转换修改时间:2026-06-17 11:39:41

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。