如何在JavaScript/RXJS中实现带日期序列的7天滑动累计与均值计算
方案1:纯Reducer实现(无依赖)
和你原有固定周统计的reducer使用方式完全兼容,可直接用于数组reduce调用,也可嵌入RxJS的scan操作符中使用。累加器会缓存最近7天的原始数据和增量总和,滑动时仅做增减计算,无需全量遍历窗口,性能更好。
const sliding7dReducer = (accumulator, currentValue, index) => { // 首次调用初始化累加器结构 if (!accumulator.window) { accumulator = { window: [], // 缓存当前窗口内的原始数据 sumTemp: 0, // temp累计值 sumHum: 0, // hum累计值 results: [] // 存储每个位置的滑动计算结果 } } // 加入当前值到窗口,更新总和 accumulator.window.push(currentValue) accumulator.sumTemp += currentValue.temp accumulator.sumHum += currentValue.hum // 窗口超过7天则移除最早的元素,扣减对应总和 if (accumulator.window.length > 7) { const removed = accumulator.window.shift() accumulator.sumTemp -= removed.temp accumulator.sumHum -= removed.hum } // 计算当前窗口的平均值,存入结果集 const windowSize = accumulator.window.length accumulator.results.push({ endDate: currentValue.date, windowSize, sumTemp: accumulator.sumTemp, sumHum: accumulator.sumHum, avgTemp: Number((accumulator.sumTemp / windowSize).toFixed(2)), avgHum: Number((accumulator.sumHum / windowSize).toFixed(2)) }) return accumulator }
使用示例:
// measurements为你的测量值数组 const slidingResult = measurements.reduce(sliding7dReducer, {}).results
方案2:RxJS原生操作符实现(推荐,适配Observable场景)
针对你使用Observable的场景,直接用RxJS内置操作符实现更简洁,不需要手动维护reducer状态。
情况1:仅输出满7天的窗口结果
使用bufferCount操作符直接生成滑动窗口,第一个结果会在第7个数据推送后输出:
import { bufferCount, map } from 'rxjs/operators' // measurements$为你的测量值Observable const slidingResult$ = measurements$.pipe( bufferCount(7, 1), // 窗口大小7,每次滑动步长1 map(window => { const sumTemp = window.reduce((sum, item) => sum + item.temp, 0) const sumHum = window.reduce((sum, item) => sum + item.hum, 0) return { endDate: window[6].date, windowSize: 7, sumTemp, sumHum, avgTemp: Number((sumTemp / 7).toFixed(2)), avgHum: Number((sumHum / 7).toFixed(2)) } }) )
情况2:保留前6个不满7天的窗口结果
用scan操作符实现和方案1一致的逻辑,从第一个数据开始就输出结果:
import { scan, map } from 'rxjs/operators' const slidingResult$ = measurements$.pipe( scan((acc, current) => { acc.window.push(current) acc.sumTemp += current.temp acc.sumHum += current.hum if (acc.window.length > 7) { const removed = acc.window.shift() acc.sumTemp -= removed.temp acc.sumHum -= removed.hum } const windowSize = acc.window.length return { window: acc.window, sumTemp: acc.sumTemp, sumHum: acc.sumHum, result: { endDate: current.date, windowSize, sumTemp: acc.sumTemp, sumHum: acc.sumHum, avgTemp: Number((acc.sumTemp / windowSize).toFixed(2)), avgHum: Number((acc.sumHum / windowSize).toFixed(2)) } } }, { window: [], sumTemp: 0, sumHum: 0 }), map(acc => acc.result) )
补充说明
如果你的测量数据存在日期断档,不是按天连续推送,可以把窗口长度判断替换为日期差校验,仅保留当前日期往前7天内的有效数据即可,核心逻辑和上面的实现一致。
内容的提问来源于stack exchange,提问作者stwissel
相关产品推荐
相关产品推荐

