Zach LeFevre
Zach LeFevre

Reputation: 81

Reduce returns empty array, however scan does not

Code:

const Rx = require('rxjs')

const data = [
    { name: 'Zachary', age: 21 },
    { name: 'John', age: 20 },
    { name: 'Louise', age: 14 },
    { name: 'Borg', age: 15 }
]

const dataSubj$ = new Rx.Subject()
function getDataStream() {
    return dataSubj$.asObservable().startWith(data);
}

getDataStream()
    .mergeMap(Rx.Observable.from)
    .scan((arr, person) => {
        arr.push(person)
        return arr
    }, [])
    .subscribe(val => console.log('val: ', val));

Using .reduce(...) instead of .scan(...) returns an empty array and nothing is printed. The observer of dataSub$ should receive an array.
Why does scan allow elements of data to pass through, but reduce does not?
Note: I am using mergeMap because I will filter the elements of the array before reducing them back into a single array.

Upvotes: 2

Views: 1094

Answers (1)

ZahiC
ZahiC

Reputation: 14687

scan emits the accumulated value on every source item.

reduce emits only the last accumulated value. It waits until the source Observable is completed and only then emits the accumulated value.

In your case the source Observable, which relies on a subject, never completes. Thus, the reduce would never emit any value.

You may want to apply the reduce on the inner Observable of the mergeMap. For each array, the inner Observable would complete when all the array items are emitted:

const data = [
  { name: 'Zachary', age: 21 },
  { name: 'John', age: 20 },
  { name: 'Louise', age: 14 },
  { name: 'Borg', age: 15 }
]

const dataSubj$ = new Rx.Subject()
function getDataStream() {
  return dataSubj$.asObservable().startWith(data);
}

getDataStream()
  .mergeMap(arr => Rx.Observable.from(arr)
    .reduce((agg, person) => {
      agg.push(person)
      return agg
    }, [])
  )
  .subscribe(val => console.log('val: ', val));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"></script>

Upvotes: 4

Related Questions