I have an observable that emits measurement values with a date for a key. Something like:
{ "date" : "2021-11-01", "temp" : 23.4, "hum" : 74.5 }
I need a 7 days running total and average for temp
and hum
. If I would have a a value for each week, I could write:
const weeklyReducer = (accumulator, currentValue, index) => { const key = Math.floor((index-1)/7); const workValue = accumulator[key] || {key, temp: 0, hum:0}; workValue.temp = workValue.temp + currentValue.temp; workValue.hum = workValue.hum + currentValue.hum; accumulator[key] = workValue; return accumulator; }
However I need a running total where the values are accumulated like this:
Running total 1: 1 Running total 2: 1,2 ... Running total 7: 1,2,3,4,5,6,7 Running total 8: 2,3,4,5,6,7,8 Running total 9: 3,4,5,6,7,8,9 Running total 10: 4,5,6,7,8,9,10
How would I design a reducer for this? I’m open for alternative approaches
Advertisement
Answer
Something like this?
Here you do re-calculate the total each time. If there were more values or calculating the totals was computationally expensive, you could keep a stack of values and push/pop to subtract old values and push new ones. For a running total of 7, it’s faster to just recalculate with each emission.
I made the observable empty so that this toy example compiles. You’ll need to provide some data instead of an EMPTY
stream.
interface measurement { date : string, temp : number, hum : number } let measurements$: Observable<measurement> = EMPTY; measurements$.pipe( scan((acc, curr) => [...acc.slice(-6), curr], [] as measurement[]), map(measurements => ({ runningDates: measurements.map(({date}) => date), totalTemp: measurements.reduce((acc,{temp}) => acc + temp, 0), totalHum: measurements.reduce((acc,{hum}) => acc + hum, 0), })) ).subscribe(console.log);