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

如何从Projects数组生成含嵌套Subtasks的对象数组?

解决Projects数组转多层嵌套任务数组的问题

需求说明

需要将包含workItems子数组的projects数组,转换为每个项目对应一个主任务对象的数组,每个主任务的subtasks属性由对应项目的workItems转换而来,且每个workItem还需进一步拆分为更细的子任务(如Design、Workshop、Site等阶段)。

现有问题代码

当前代码通过两层map导致每个workItem生成一个主任务对象,不符合“一个项目对应一个主任务”的预期:

let newProjects1 = projects.map((project, index) => {
  return project.workItems.map((workItem) => {
    return {
      TaskID: index,
      ProjectNo: project.projectNumber,
      TaskName: project.projectTitle,
      StartDate: new Date("11/11/2024"),
      EndDate: new Date(project.projectEnd),
      subtasks: [
        {
          TaskID: index,
          TaskName: workItem.name,
          StartDate: new Date("11/11/2024"),
          Duration: 80,
          Progress: 150,
        },
      ],
    };
  })
});

输入数据示例(单个Project对象)

[{
  "projectNumber": 26278,
  "projectTitle": "Ifon WTW",
  "chemicals": ["sodium hypochlorite", "sodium hydroxide", "pacl"],
  "projectStatus": "site survey",
  "siteType": "SR",
  "location": "Glasgow",
  "contractType": "construction",
  "designLead": "Craig Garvie",
  "projectManager": "Isaac Stanton",
  "projectType": "Other",
  "spm": "Mark Tench",
  "client": "Yorkshire Water",
  "comments": "project going swimmingly",
  "projectStart": "12/20/2022",
  "projectEnd": "07/28/2024",
  "createdAt": "2022-11-22T07:43:42Z",
  "updatedAt": "2023-04-09T10:13:14Z",
  "equipment": [
    { "name": "kiosk", "count": 2 },
    { "name": "tanker fill point", "count": 1 },
    { "name": "dosing skid", "count": 1 },
    { "name": "POA catchpot", "count": 1 },
    { "name": "Low Point Catchpot", "count": 0 },
    { "name": "MCC", "count": 1 }
  ],
  "workItems": [
    {
      "name": "work Item 1",
      "siteSurveyStart": "11/29/2022",
      "siteSurveyEnd": "01/25/2023",
      "designStart": "02/14/2023",
      "designEnd": "03/06/2023",
      "workShopStart": "04/24/2023",
      "workShopEnd": "05/05/2023",
      "rsePremisesStart": "06/24/2023",
      "rsePremisesEnd": "07/09/2023",
      "siteStart": "08/20/2023",
      "siteEnd": "09/13/2023"
    },
    {
      "name": "work Item 2",
      "siteSurveyStart": "11/02/2022",
      "siteSurveyEnd": "01/05/2023",
      "designStart": "02/02/2023",
      "designEnd": "03/24/2023",
      "workShopStart": "04/11/2023",
      "workShopEnd": "05/19/2023",
      "rsePremisesStart": "06/19/2023",
      "rsePremisesEnd": "07/17/2023",
      "siteStart": "08/20/2023",
      "siteEnd": "09/23/2023"
    }
  ],
  "chemical": [{ "name": "sodium carbonate" }, { "name": "sulphuric acid" }],
  "projectPersonnel": [
    { "name": "daniel carey" },
    { "name": "erin donnelly" },
    { "name": "craig garvie" },
    { "name": "ryan watson" },
    { "name": "lewis scott" },
    { "name": "jack overfield" },
    { "name": "fidel hernandez" }
  }
}]

修正后的代码

// 计算两个日期之间的天数差,用于Duration
const getDuration = (startDateStr, endDateStr) => {
  const start = new Date(startDateStr);
  const end = new Date(endDateStr);
  return Math.ceil((end - start) / (1000 * 60 * 60 * 24));
};

let newProjects = projects.map((project, projIndex) => {
  // 主任务ID,从1开始
  const mainTaskId = projIndex + 1;
  return {
    TaskID: mainTaskId,
    ProjectNo: project.projectNumber.toString(),
    TaskName: project.projectTitle,
    StartDate: new Date(project.projectStart),
    EndDate: new Date(project.projectEnd),
    // 生成当前项目的subtasks(对应workItems)
    subtasks: project.workItems.map((workItem, itemIndex) => {
      // workItem的ID格式:主ID.序号
      const workItemId = `${mainTaskId}.${itemIndex + 1}`;
      // 从workItem中提取各阶段生成子任务
      const stageSubtasks = [
        {
          TaskID: `${workItemId}.1`,
          TaskName: "Design",
          StartDate: new Date(workItem.designStart),
          Duration: getDuration(workItem.designStart, workItem.designEnd),
          Progress: 150
        },
        {
          TaskID: `${workItemId}.2`,
          TaskName: "Workshop",
          StartDate: new Date(workItem.workShopStart),
          Duration: getDuration(workItem.workShopStart, workItem.workShopEnd),
          Progress: 150
        },
        {
          TaskID: `${workItemId}.3`,
          TaskName: "Site",
          StartDate: new Date(workItem.siteStart),
          Duration: getDuration(workItem.siteStart, workItem.siteEnd),
          Progress: 150
        }
      ];

      return {
        TaskID: parseFloat(workItemId),
        TaskName: workItem.name,
        StartDate: new Date(project.projectStart),
        Duration: getDuration(project.projectStart, project.projectEnd),
        Progress: 150,
        comments: project.comments,
        subtasks: stageSubtasks
      };
    })
  };
});

代码说明

  1. 外层map遍历Projects:每个项目生成一个主任务对象,而非拆分多个主任务
  2. subtasks生成逻辑:主任务的subtasks由当前项目的workItems数组转换而来,每个workItem对应一个子任务
  3. 多层子任务拆分:每个workItem进一步拆分为Design、Workshop、Site等阶段子任务,通过提取workItem中的日期字段计算Duration
  4. TaskID规则:主任务ID从1开始,workItemID为主ID.序号,阶段子任务ID为workItemID.序号,保证层级清晰
  5. 日期与Duration计算:通过工具函数getDuration计算两个日期之间的天数差,替代固定值,更贴合实际数据

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 04:58:14