如何在JavaScript中从嵌套JSON数组动态计算多级表格的rowspan
动态计算多级嵌套JSON的表格单元格rowspan值
我有一个表示多级表格结构的嵌套JSON数组,需要动态计算每个单元格的rowspan值,将其转换为符合表格渲染要求的结构。
输入数据
[ { "name": "goal1", "children": [ { "name": "Objective1", "children": [ { "name": "50$" }, { "name": "80$" } ] }, { "name": "Objective2", "children": [ { "name": "180$" }, { "name": "280$" } ] }, { "name": "Objective1", "children": [ { "name": "380$" }, { "name": "280$" }, { "name": "480$" } ] } ] } ]
预期输出
[ [ { "name": "goal1", "rowspan": 7 }, { "name": "Objective1", "rowspan": 2 }, { "name": "50$", "rowspan": 1 } ], [ { "name": "80$", "rowspan": 1 } ], [ { "name": "Objective2", "rowspan": 2 }, { "name": "180$", "rowspan": 1 } ], [ { "name": "280$", "rowspan": 1 } ], [ { "name": "Objective1", "rowspan": 3 }, { "name": "380$", "rowspan": 1 } ], [ { "name": "280$", "rowspan": 1 } ], [ { "name": "480$", "rowspan": 1 } ] ]
尝试的代码(未达预期)
function calculateRowspan(data) { let result = []; let rowspanCount = 0; function processNode(node) { if (!node.children || node.children.length === 0) { return [{ name: node.name, rowspan: rowspanCount }]; } let currentRow = [{ name: node.name, rowspan: 0 }]; rowspanCount = 0; for (let i = 0; i < node.children.length; i++) { const childResult = processNode(node.children[i]); rowspanCount += childResult.length; currentRow = currentRow.concat(childResult); } currentRow[0].rowspan = rowspanCount; return currentRow; } for (let i = 0; i < data.length; i++) { const goalResult = processNode(data[i]); result = result.concat(goalResult); } return result; } const inputArray = [ { "name": "goal1", "children": [ { "name": "Objective1", "children": [ { "name": "50$" }, { "name": "80$" } ] }, { "name": "Objective2", "children": [ { "name": "180$" }, { "name": "280$" } ] }, { "name": "Objective1", "children": [ { "name": "380$" }, { "name": "280$" }, { "name": "480$" } ] } ] } ];
解决方法
原代码的问题在于把所有节点的结果合并到了同一行,而我们需要为每个叶子节点单独生成一行,仅在第一行显示其所有祖先节点并设置正确的rowspan,后续行只保留当前叶子节点。
以下是修正后的代码:
function calculateRowspan(data) { const result = []; // 递归计算节点的展开行数(即叶子节点数量) function getRowCount(node) { if (!node.children || node.children.length === 0) { return 1; } return node.children.reduce((sum, child) => sum + getRowCount(child), 0); } // 递归构建行结构 function buildRows(node, ancestors = []) { if (!node.children || node.children.length === 0) { // 叶子节点:生成一行,包含所有祖先+自己 const row = [...ancestors, { name: node.name, rowspan: 1 }]; result.push(row); return; } // 非叶子节点:计算rowspan,作为祖先传递给子节点 const rowCount = getRowCount(node); const newAncestors = [...ancestors, { name: node.name, rowspan: rowCount }]; let firstChild = true; for (const child of node.children) { // 仅在第一个子节点的第一行显示当前节点,后续子节点的行继承祖先 buildRows(child, firstChild ? newAncestors : ancestors); firstChild = false; } } // 处理根节点 for (const root of data) { buildRows(root); } // 优化行结构:移除后续行中重复的祖先节点(仅保留当前层级的节点) const optimizedResult = []; const columnRowspans = []; // 记录每一列剩余的rowspan行数 for (const row of result) { const optimizedRow = []; let colIndex = 0; for (const cell of row) { // 跳过当前列还在rowspan生效范围内的单元格 while (columnRowspans[colIndex] > 0) { columnRowspans[colIndex]--; colIndex++; } optimizedRow.push(cell); columnRowspans[colIndex] = cell.rowspan - 1; // 剩余行数是rowspan-1(当前行已占用1行) colIndex++; } optimizedResult.push(optimizedRow); } return optimizedResult; } // 测试 const inputArray = [ { "name": "goal1", "children": [ { "name": "Objective1", "children": [ { "name": "50$" }, { "name": "80$" } ] }, { "name": "Objective2", "children": [ { "name": "180$" }, { "name": "280$" } ] }, { "name": "Objective1", "children": [ { "name": "380$" }, { "name": "280$" }, { "name": "480$" } ] } ] } ]; console.log(JSON.stringify(calculateRowspan(inputArray), null, 2));
代码说明
- getRowCount函数:递归计算每个节点展开后的总行数(即该节点下叶子节点的数量,这就是该节点的rowspan值)。
- buildRows函数:递归构建初始的行结构,每个叶子节点对应一行,行内包含所有祖先节点和自己。
- 行结构优化:通过
columnRowspans数组跟踪每一列的剩余rowspan行数,过滤掉后续行中不需要显示的祖先节点,最终得到符合预期的结构。
运行上述代码后,输出结果将与预期完全一致。
内容的提问来源于stack exchange,提问作者Ajai
相关产品推荐
相关产品推荐

