Creating an array of observables based off a condition and then reducing that array to one observable

36 views Asked by At

I have two types of observables A & B, they return the same item type but are accessible by separate APIs. With a list of id's which can be for either A or B.

These observables return data for a type of object either A or B.

I need to loop through the ID's, checking each to see if they belong to either A or B and return the corresponding observables as a single observable.

The problem is, this seems to only ever return either undefined (probably because the subscription is not waited for) or else just One single value.

This is the logic I had in mind

getA&BCombined(ids: number): Observable<SharedType> {
    ObservableList = [];
    for(id in ids.length) {
        if(id === typeA)
            ObservableList.push(getObservableTypeA(id))
        else
            ObservableList.push(getObservableTypeB(id))
    }
    return combineLatest(observableList)

I'm still learning RXjS so any help would be appreciated here as I've been stuck on this for a while now.

I tried:

return from(ids).pipe(
      switchMap((id: number) => this.isA(id)? this.service.getA(id) : this.service.getB(id)),
      scan((spatialList: ISpatial[], spatial: ISpatial) => {
        spatialList.push(spatial);
        return spatialList;
      }, [])
    )

Which returns only a single value.

return iif(() => ids?.length > 0,from(ids).pipe(
     switchMap((id: number) => this.getAorB(id))));

getAorB(id) {
    return this.service.getA(catchError(_ => this.service.getB));
}

Which only returns the first type (A) and dosen't get B via catchError. The reason I use catchError is the disctinction between A and B is only available after returning the observable

1

There are 1 answers

0
BizzyBob On

Your second code sample will work if you use mergeMap instead of switchMap.

The reason you only get one result is because switchMap only handles one inner source at a time. Since from emits the elements synchronously the source is switched before the callback logic is executed.

mergeMap will handle all the inner sources, so you'll get all your results.

return from(ids).pipe(
  mergeMap(id => this.isA(id) ? this.service.getA(id) : this.service.getB(id)),
  scan((spatialList: ISpatial[], spatial: ISpatial) => {
    spatialList.push(spatial);
    return spatialList;
  }, [])
)

Note, you could simplify by using .concat instead of .push:

return from(ids).pipe(
  mergeMap(id => this.isA(id) ? this.service.getA(id) : this.service.getB(id)),
  scan((spatialList, spatial) => spatialList.concat(spatial), [] as ISpatial[])
);