You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript数组非扁平化问题:如何正确构建父子层级结构?

非扁平化处理带层级编号的任务数组问题

需要把扁平化的任务数组,按照number属性的层级关系(比如1是1.1的父级,2.1是2.1.1的父级)转换成嵌套结构,但当前用map()+filter()的实现会重复出现已作为子项的元素,比如最终数组里同时有1和1.1,而1.1已经是1的子项了,不符合预期。

现有代码

const arr = [
  { name: 'task a', number: '1' },
  { name: 'task b', number: '1.1'},
  { name: 'task c', number: '1.2' },
  { name: 'task d', number: '2' },
  { name: 'task e', number: '2.1' },
  { name: 'task f', number: '2.1.1' }
];

const output = arr.map((element) => {
  const re = new RegExp(`^(${element.number}\\.)\\d$`)
  element.subRows = arr.filter((e) => {
    return e.number.match(re)
  })
  return element
})

output.forEach((x) => {
  console.log(x)
})

预期输出

const data = [
  {
    name: 'task a',
    number: '1',
    subRows: [
      {
        name: 'task b',
        number: '1.1',
        subRows: []
      },
      {
        name: 'task c',
        number: '1.2',
        subRows: []

      }
    ]
  },
  {
    name: 'task d',
    number: '2',
    subRows: [
      {
        name: 'task e',
        number: '2.1',
        subRows: [
          {
            name: 'task f',
            number: '2.1.1',
            subRows: []
          }
        ]
      },
    ]
  },
];

问题分析

原代码的核心问题是:遍历了所有元素并给每个元素都添加了subRows,导致子元素(比如1.1、2.1)本身也会出现在最终的output数组里,不符合预期的嵌套结构。

解决方案

我们可以通过建立元素映射表的方式,快速找到父级元素并将子元素挂载到父级的subRows中,最后只保留顶级元素(没有父级的元素),这样就不会有重复项,同时自动处理所有层级:

const arr = [
  { name: 'task a', number: '1' },
  { name: 'task b', number: '1.1'},
  { name: 'task c', number: '1.2' },
  { name: 'task d', number: '2' },
  { name: 'task e', number: '2.1' },
  { name: 'task f', number: '2.1.1' }
];

// 1. 创建以number为键的映射表,同时给每个元素初始化空的subRows
const elementMap = arr.reduce((map, item) => {
  map[item.number] = { ...item, subRows: [] };
  return map;
}, {});

// 2. 遍历每个元素,找到对应的父级并挂载到父级的subRows
arr.forEach(item => {
  const numberParts = item.number.split('.');
  // 只有非顶级元素才有父级(number包含至少一个点)
  if (numberParts.length > 1) {
    // 父级number是去掉最后一段的部分,比如2.1.1的父级是2.1
    const parentNumber = numberParts.slice(0, -1).join('.');
    // 如果父级存在,就把当前元素添加到父级的subRows里
    if (elementMap[parentNumber]) {
      elementMap[parentNumber].subRows.push(elementMap[item.number]);
    }
  }
});

// 3. 筛选出所有顶级元素(number拆分后只有一段,比如1、2)
const output = Object.values(elementMap).filter(item => item.number.split('.').length === 1);

console.log(output);

这段代码的逻辑:

  • 用reduce创建映射表,方便O(1)时间查找任意元素
  • 遍历每个元素,通过拆分number找到父级编号,把当前元素挂载到父级的subRows
  • 最后只保留没有父级的顶级元素,这些元素就是嵌套结构的根节点

运行后输出完全符合预期,且不会出现重复元素,同时支持任意深度的层级嵌套。

内容的提问来源于stack exchange,提问作者WindCheck

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 16:00:19