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

JS数组映射时指定日期范围多维度聚合统计结果错误

问题根因

代码存在三个核心问题导致统计结果不符合预期:

  • 聚合唯一键缺失label维度:生成分组key时仅拼接了日期、studyId、siteId,没有加入状态标签字段,导致同一站点、同一研究下不同状态的条目被错误合并计数。
  • 时间匹配逻辑不符合按天统计需求:当前逻辑仅判断单个时间点是否落在状态有效区间内,没有按自然日范围做区间重叠判断,无法正确覆盖全天内生效的所有状态。
  • 存储结构不规范:用数组类型存储聚合键值对,虽然JS语法允许,但容易出现原型链属性冲突等隐患,应该使用普通对象存储分组结果。
修正方案
  1. 对每个批次日期,先计算对应统计自然日的UTC起止时间(当日0点到次日0点)
  2. 调整区间匹配逻辑,判断状态有效区间和统计日区间是否存在重叠,正确过滤当日有效条目
  3. 聚合key中加入label字段,保证分组维度和需求一致(日期+studyId+siteId+label)
  4. 使用普通对象存储聚合结果,避免数组存键值对的隐患
修正后完整代码
const mockData = [
  // INCOMPLETE
  {
    id: "C1-TBX4",
    studyId: "TBX4",
    siteId: null,
    statusType: "INCOMPLETE",
    statusFrom: "2020-12-01T12:15:00.000Z",
    statusTo: "2020-12-05T01:00:00.000Z"
  },
  {
    id: "C2-TBX4",
    studyId: "TBX4",
    siteId: "USA-1",
    statusType: "INCOMPLETE",
    statusFrom: "2020-12-01T01:00:00.000Z",
    statusTo: null
  },
  {
    id: "C3-TBX4",
    studyId: "TBX4",
    siteId: "USA-1",
    statusType: "INCOMPLETE",
    statusFrom: "2020-12-01T01:00:00.000Z",
    statusTo: null
  },
  {
    id: "C4-TBX4",
    studyId: "TBX4",
    siteId: null,
    statusType: "INCOMPLETE",
    statusFrom: "2020-12-01T12:15:00.000Z",
    statusTo: "2020-12-05T01:00:00.000Z"
  },
  // DROPOUT_PRESCREENER
  {
    id: "C5-TBX4",
    studyId: "TBX4",
    siteId: "USA-1",
    statusType: "DROPOUT_PRESCREENER",
    eligible: false,
    statusFrom: "2020-12-01T01:00:00.000Z",
    statusTo: null
  },
  // REJECTED_PRESCREENER
  {
    id: "C6-TBX4",
    studyId: "TBX4",
    siteId: "USA-1",
    statusType: "REJECTED_PRESCREENER",
    statusFrom: "2020-12-01T01:00:00.000Z",
    statusTo: null
  },
  // Out of range
  {
    id: "C7-TBX4",
    studyId: "TBX4",
    siteId: "USA-1",
    statusType: "REJECTED_PRESCREENER",
    statusFrom: "2020-12-02T01:00:00.000Z",
    statusTo: null
  }
];

const assignLabel = (statusType, eligible) => {
  if (statusType === "INCOMPLETE") return "pending";
  if (statusType === "REJECTED_PRESCREENER") return "ineligible";
  if (statusType === "DROPOUT_PRESCREENER" && eligible) return "eligible";
  if (statusType === "DROPOUT_PRESCREENER" && !eligible) return "abandoned";
  return "completed";
};

// 传入要统计的自然日0点即可,和日期生成函数逻辑对齐,本示例统计2020-12-01数据
const batches = [new Date("2020-12-01T00:00:00.000Z")];

const screeningNumbersCalculations = () => {
  const results = {};

  batches.forEach((batchStart) => {
    // 计算当前统计日的结束时间(次日0点)
    const batchEnd = new Date(batchStart.getTime() + 24 * 60 * 60 * 1000);
    const dateStr = batchStart.toISOString().split("T")[0];

    // 过滤出和当前统计日区间有重叠的状态条目
    const matchingStatuses = mockData.filter((s) => {
      const from = new Date(s.statusFrom);
      const to = s.statusTo ? new Date(s.statusTo) : Infinity;
      // 区间重叠判断:状态开始时间早于统计日结束,统计日开始时间早于状态结束时间
      return from < batchEnd && batchStart < to;
    });

    matchingStatuses.forEach((ms) => {
      const label = assignLabel(ms.statusType, ms.eligible);
      // 聚合key加入label字段,保证维度完整
      const key = `${dateStr}${ms.studyId}${ms.siteId}${label}`;
      
      if (results[key]) {
        results[key].total++;
      } else {
        results[key] = {
          days: [dateStr],
          studyId: ms.studyId,
          siteId: ms.siteId,
          screeningStage: "SELF_ASSESSMENT",
          label: label,
          total: 1
        };
      }
    });
  });

  return Object.values(results);
};

const screeningNumbers = screeningNumbersCalculations();
console.log(JSON.stringify(screeningNumbers, null, 2));
运行结果

执行后输出和预期完全一致:

[
  {
    "days": ["2020-12-01"],
    "studyId": "TBX4",
    "siteId": null,
    "screeningStage": "SELF_ASSESSMENT",
    "label": "pending",
    "total": 2
  },
  {
    "days": ["2020-12-01"],
    "studyId": "TBX4",
    "siteId": "USA-1",
    "screeningStage": "SELF_ASSESSMENT",
    "label": "pending",
    "total": 2
  },
  {
    "days": ["2020-12-01"],
    "studyId": "TBX4",
    "siteId": "USA-1",
    "screeningStage": "SELF_ASSESSMENT",
    "label": "abandoned",
    "total": 1
  },
  {
    "days": ["2020-12-01"],
    "studyId": "TBX4",
    "siteId": "USA-1",
    "screeningStage": "SELF_ASSESSMENT",
    "label": "ineligible",
    "total": 1
  }
]

注:如果你的批处理逻辑是传入执行日0点统计前一天数据,只需要在计算batchStart时减去1天的毫秒数即可,不需要改动其他逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 17:16:17