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

JS如何基于最新时间戳映射对象数组生成聚合统计对象

问题描述

现有JavaScript逻辑需要对mockData状态记录数组做重映射聚合,按statusType、studyId、label、siteId四个维度分组统计对应ID总数,生成带total字段的统计结果。
现有逻辑存在统计错误:同一ID在统计日期当天存在多次状态变更时,会把当日所有状态记录都纳入统计,导致重复计数、输出多余统计项。比如ID为C10-TBX4的记录在2020-12-05当天有3次状态变更,现有逻辑会输出2条统计结果,不符合预期。

正确统计规则:同一ID在统计日期范围内存在多条状态记录时,仅保留statusFrom时间戳最新的1条有效状态记录参与后续聚合统计,上述示例场景预期仅输出1条label为pending、total为1的统计记录。

实现思路

整个处理流程分3步,从根源避免重复计数问题:

  • 先按统计日期范围过滤出所有符合时间条件的原始状态记录
  • 对过滤后的记录按ID做去重:同一个ID对应多条记录的,只保留statusFrom时间戳最大的那条有效记录
  • 对去重后的有效记录,按指定的四个维度做分组聚合,计算每个分组下的ID总数作为total值
参考实现代码
// 示例mock数据,可替换为实际业务数据
const mockData = [
  { id: 'C10-TBX4', statusType: 'visit', studyId: 'S001', label: 'approved', siteId: 'SI02', statusFrom: 1607126400000 }, // 2020-12-05 08:00
  { id: 'C10-TBX4', statusType: 'visit', studyId: 'S001', label: 'rejected', siteId: 'SI02', statusFrom: 1607155200000 }, // 2020-12-05 16:00
  { id: 'C10-TBX4', statusType: 'visit', studyId: 'S001', label: 'pending', siteId: 'SI02', statusFrom: 1607169600000 }, // 2020-12-05 20:00 当日最新有效状态
  { id: 'C10-TBX5', statusType: 'visit', studyId: 'S001', label: 'approved', siteId: 'SI02', statusFrom: 1607126400000 }
]

// 统计日期配置,示例为统计2020-12-05全天数据
const statDateStart = new Date('2020-12-05 00:00:00').getTime()
const statDateEnd = new Date('2020-12-05 23:59:59').getTime()

function getStatusStat(data, startTime, endTime) {
  // 1. 过滤时间范围内的记录
  const inRangeRecords = data.filter(item => {
    return item.statusFrom >= startTime && item.statusFrom <= endTime
  })

  // 2. 按ID去重,仅保留每个ID下statusFrom最新的有效记录
  const latestRecordMap = new Map()
  inRangeRecords.forEach(record => {
    const existRecord = latestRecordMap.get(record.id)
    if (!existRecord || record.statusFrom > existRecord.statusFrom) {
      latestRecordMap.set(record.id, record)
    }
  })
  const validRecords = Array.from(latestRecordMap.values())

  // 3. 按指定维度聚合统计total
  const statMap = new Map()
  validRecords.forEach(record => {
    // 拼接四个维度作为唯一分组key
    const groupKey = `${record.statusType}_${record.studyId}_${record.label}_${record.siteId}`
    const existGroup = statMap.get(groupKey)
    if (existGroup) {
      existGroup.total += 1
    } else {
      statMap.set(groupKey, {
        statusType: record.statusType,
        studyId: record.studyId,
        label: record.label,
        siteId: record.siteId,
        total: 1
      })
    }
  })

  return Array.from(statMap.values())
}

// 执行统计
const result = getStatusStat(mockData, statDateStart, statDateEnd)
console.log(result)
// 符合预期的输出:
// [
//   {statusType: 'visit', studyId: 'S001', label: 'pending', siteId: 'SI02', total: 1},
//   {statusType: 'visit', studyId: 'S001', label: 'approved', siteId: 'SI02', total: 1}
// ]
补充说明
  • 用Map做ID维度的去重存储,仅需遍历一次时间范围内的记录就能完成有效状态筛选,时间复杂度O(n),性能优于数组排序后去重的方案
  • 分组统计同样用Map做分组key映射,避免嵌套循环查找分组,大数据量下执行效率更高
  • 如果需要支持多日期分段统计,只需要调整第一步的时间范围过滤逻辑,把统计日期也加入分组key即可,核心去重、聚合逻辑不需要改动

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 20:06:26