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

将无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);

工作原理

  1. 初始化根节点:作为树形结构的顶层容器,包含子节点数组和总hours汇总
  2. 遍历原始数据:对每一条数据项,按分组键逐层处理
  3. 创建/查找分组节点:每一层级检查是否已存在对应分组值的节点,不存在则创建并初始化hours和nodes
  4. 累加hours:每经过一个分组节点,就将当前数据项的hours累加到该节点的汇总值中
  5. 添加叶子节点:遍历完所有分组键后,将原始数据项添加到最内层分组节点的nodes数组中

注意事项

  • 确保原始数据中的hours字段为数字类型,否则累加会出错
  • 分组键数组中的字段必须在原始数据中存在,否则会出现undefined的分组名称

内容的提问来源于stack exchange,提问作者Cedric F.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 05:50:29