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
相关产品推荐
相关产品推荐

