JS遍历nest标识父子对象数组 按展开状态收集子数据
问题说明
现有结构与下方示例中parentChildData完全一致的数组,层级规则如下:
nest: 0标识父级数据nest: 1标识隶属于最近一个nest:0父节点的子级数据
遍历该数组时,需要通过遍历索引从另一个对象数组baseData中拉取对应位置的数据。业务逻辑中父节点支持展开/收起两种状态:
- 展开状态下需要收集该父节点下的所有子数据
- 收起状态下不收集子数据
- 所有处于展开状态的父节点pk,存储在形如
["P000", "P003"]的openRows数组中
目前已经实现两个基础规则:
- 始终将表头行加入结果数组
- 无论父节点是否展开,始终将所有父级行加入结果数组
待实现核心逻辑:当遍历到pk存在于openRows中的父节点时,需要持续将后续的子行数据加入结果数组,直到遇到下一个nest:0的父级行时停止收集。
现有未完成代码
const openRows = ["P000", "P0003"]; const newData = []; const parentChildData = [ [ { "header": true }, { "pk": "P000", "nest": 0, "expanded": true }, { "pk": "P000-000", "nest": 1 }, { "pk": "P000-002", "nest": 1 }, { "pk": "P000-003", "nest": 1 }, { "pk": "P000-001", "nest": 1 }, { "pk": "P001", "nest": 0 }, { "pk": "P001-000", "nest": 1 }, { "pk": "P001-001", "nest": 1 }, { "pk": "P001-002", "nest": 1 }, { "pk": "P002", "nest": 0 }, { "pk": "P002-000", "nest": 1 }, { "pk": "P003", "nest": 0 }, { "pk": "P003-000", "nest": 1 }, { "pk": "P003-001", "nest": 1 }, { "pk": "P004", "nest": 0 }, { "pk": "P004-000", "nest": 1 }, { "pk": "P005", "nest": 0 }, { "pk": "P005-000", "nest": 1 }, { "pk": "P006-000", "nest": 0 } ] ] parentChildData.forEach((row, index) => { if(index === 0 && row.header) { // 固定加入表头行 newData.push(baseData[index]); } if(row.nest === 0) { // 无论展开收起,固定加入父级行 newData.push(row) } if(openRows.includes(row.pk)){ // 待实现:当前父节点展开时,持续加入后续子行,直到遇到下一个nest:0的父节点 } })
实现方案
不需要在遇到展开父节点时嵌套向后遍历,只需加一个布尔标记位,在遍历过程中维护「当前是否需要收集子节点」的状态即可,逻辑清晰且性能更好:
- 初始化标记
collectChild = false,用来标识当前遍历位置是否处于展开父节点的子节点区间 - 现有代码中
parentChildData多嵌套了一层数组,实际遍历目标是内层的行数组parentChildData[0] - 遍历每一行时按顺序判断:
- 如果是表头行,直接加入
baseData对应项,跳过后续逻辑 - 如果是
nest:0的父节点:先加入baseData对应项,再判断当前父节点pk是否在openRows中,更新collectChild标记为对应布尔值,跳过当前行后续逻辑 - 如果是
nest:1的子节点,且collectChild为true,加入baseData对应项即可
- 如果是表头行,直接加入
完整可运行代码:
const openRows = ["P000", "P003"]; const newData = []; // 取内层实际行列表遍历 const rowList = parentChildData[0]; let collectChild = false; rowList.forEach((_, index) => { const row = rowList[index]; // 处理表头 if (index === 0 && row.header) { newData.push(baseData[index]); return; } // 处理父级行 if (row.nest === 0) { newData.push(baseData[index]); // 遇到新的父节点,更新子节点收集标记 collectChild = openRows.includes(row.pk); return; } // 处理子级行:只有标记为需要收集时才加入 if (row.nest === 1 && collectChild) { newData.push(baseData[index]); } });
逻辑说明:每遇到一个父节点就重置收集标记,刚好匹配「子节点隶属于最近一个nest:0父节点」的规则,不需要额外判断子节点归属,遍历一次即可完成所有数据收集,时间复杂度O(n)。
内容的提问来源于stack exchange,提问作者Udders
相关产品推荐
相关产品推荐

