Search code examples
javascriptrxjsreactive-programmingreactivex

ReactiveX: filter out numbers from observable so that total never goes below zero


The following RxJs code displays numbers from the input list and the running total:

let inputs = [10, 10, -30, 10, -30, 50, -25, -30, 10]

let obs$ = Rx.Observable.from(inputs)
let totals$ = obs$.scan((acc, next) => acc + next, 0)
obs$.subscribe(x => console.log('x:', x));
totals$.subscribe(x => console.log("total:", x))

totals$ will emit: 10, 20, -10, 0, -30...

I would like to transform the obs$ observable somehow, so that the resulting totals$ never emits a negative number.

I.e., in this case, both first and last "-30" should be filtered out:

otherObs$: 10, 10, 10, -30, 50, -25, 10

totals$: 10, 20, 30, 0, 50, 25, 35

edit: Note that I'm interested in the modified otherObs$ observable, i.e., a sequence of the original input numbers with some filtered out (obs$ will actually contain more data, so I really do need the original elements; the "value" is just a key to filter on). The totals$ here is only for showing what is going on.


Solution

  • Based on @CozyAzure's ideas, I figured out a better answer. I simply need to store more state in the reducer: the original value, the accumulated total, and whether this value was added to the total or not.

    let other$ = obs$
      .scan(({value, total, valid}, next) => {
         if (total + next < 0)
           return { value: next, total: total, valid: false }
         else
           return { value: next, total: total + next, valid: true }
      }, { value: undefined, total: 0, valid: false })
      .filter(x => x.valid)
      .map(x => x.value)
    

    This way, I can filter out the values that were not added, and get back the original values that were.