将无parent_id的JavaScript扁平数组转为树形结构并分组求和
动态分组扁平数组生成带汇总的树形结构适配React Tree Table
需求说明
需要将无父ID的扁平JavaScript数组重构为树形结构,用于React Tree Table组件。要求:
- 根据指定的分组键数组(如
['activity', 'workplace'])动态分组 - 每个分组节点自动汇总子节点的
hours字段 - 叶子节点保留原始数据的完整信息
现有扁平数据
const data = [ { id: 1, activity: 'Activity 1', workplace: 'Workplace 1', machine: 'Machine 1', error: 'Error 1', number: 'Doc number 1', hours: 4, }, { id: 2, activity: 'Activity 1', workplace: 'Workplace 1', machine: 'Machine 2', error: 'Error 2', number: 'Doc number 2', hours: 3, }, { id: 3, activity: 'Activity 2', workplace: 'Workplace 2', machine: 'Machine 3', error: 'Error 3', number: 'Doc number 3', hours: 3, }, { id: 4, activity: 'Activity 2', workplace: 'Workplace 3', machine: 'Machine 4', error: 'Error 4', number: 'Doc number 4', hours: 6, } ];
预期树形结构
分组节点包含name(分组值)、hours(汇总值)、nodes(子节点数组);叶子节点为原始数据对象:
[ { name: 'Activity 1', hours: 7, nodes: [ { name: 'Workplace 1', hours: 7, nodes: [ { id: 1, activity: 'Activity 1', workplace: 'Workplace 1', ..., hours: 4 }, { id: 2, activity: 'Activity 1', workplace: 'Workplace 1', ..., hours: 3 } ] } ] }, { name: 'Activity 2', hours: 9, nodes: [ { name: 'Workplace 2', hours: 3, nodes: [ { id: 3, activity: 'Activity 2', workplace: 'Workplace 2', ..., hours: 3 } ] }, { name: 'Workplace 3', hours: 6, nodes: [ { id: 4, activity: 'Activity 2', workplace: 'Workplace 3', ..., hours: 6 } ] } ] } ]
当前代码问题
以下代码仅能生成分组结构,但无法实现hours汇总,且叶子节点未保留完整原始数据:
const groupOverview = () => data .reduce((r, o) => { activatedGroup.reduce((p, key) => { let name = o[key], temp = (p.nodes = p.nodes || []).find(q => q.name === name); if (!temp) { p.nodes.push(temp = { name }); } return temp; }, r) return r; }, { nodes: [] }) .nodes;
解决方案
完整实现函数
const groupData = (data, groups) => { const root = { nodes: [], hours: 0 }; data.forEach(item => { let currentNode = root; // 按分组键逐层构建树形结构 for (let i = 0; i < groups.length; i++) { const groupKey = groups[i]; const groupValue = item[groupKey]; // 查找当前层级是否已存在该分组节点 let existingNode = currentNode.nodes.find(node => node.name === groupValue); if (!existingNode) { // 创建新的分组节点,初始化hours和nodes existingNode = { name: groupValue, hours: 0, nodes: [] }; currentNode.nodes.push(existingNode); } // 累加当前节点的hours existingNode.hours += item.hours; // 移动到下一层级节点 currentNode = existingNode; } // 将原始数据添加到最内层分组的nodes中 currentNode.nodes.push({ ...item }); // 根节点汇总总hours(可选,根据需求决定是否保留) root.hours += item.hours; }); return root.nodes; };
使用示例
// 定义分组键 const groups = ['activity', 'workplace']; // 生成树形结构 const treeData = groupData(data, groups); console.log(treeData);
工作原理
- 初始化根节点:作为树形结构的顶层容器,包含子节点数组和总hours汇总
- 遍历原始数据:对每一条数据项,按分组键逐层处理
- 创建/查找分组节点:每一层级检查是否已存在对应分组值的节点,不存在则创建并初始化
hours和nodes - 累加hours:每经过一个分组节点,就将当前数据项的
hours累加到该节点的汇总值中 - 添加叶子节点:遍历完所有分组键后,将原始数据项添加到最内层分组节点的
nodes数组中
注意事项
- 确保原始数据中的
hours字段为数字类型,否则累加会出错 - 分组键数组中的字段必须在原始数据中存在,否则会出现
undefined的分组名称
内容的提问来源于stack exchange,提问作者Cedric F.
相关产品推荐
相关产品推荐

