3

I have an api that returns me an Array<string> of ids, given an original id (one to many). I need to make an http request on each of these ids to get back the associated data from the api. I cannot figure out how to take the Observable<string[]> and map it to the Observable<DataType[]>.

I would like to keep the original observable and use operators to get the desired outcome if at all possible.

Piping the map operator doesn't work in this situation due to the fact that the only item in the observable is the array.

Here's some example code that is similar to the implementation I am attempting.

getIds = (originalId: string) => {
 return this.http.get<string[]>(url);
}

getDataFromIds = (originalId: string): Observable<DataType[]> => {
  const ids$ = this.getIds(originalId);
  // Make http calls for each of the items in the array.
  result = ids$.pipe();

  return result;
}
Buttars
  • 53
  • 6
  • 1
    Roughly speaking you want to switchMap to a forkJoin. However, you haven't described what should happen if one of those requests fails. – Ingo Bürk Mar 05 '19 at 17:29
  • Good point. I hadn't thought of the possibility of a http failure. If the request fails it should simply continue on to the next id excluding the failed response. – Buttars Mar 05 '19 at 17:31

2 Answers2

3

this is a use case for the switchMap operator typically, with the forkjoin operator as your inner observable.

getIds = (originalId: string) => {
 return this.http.get<string[]>(url);
}

getDataFromIds = (originalId: string): Observable<DataType[]> => {
  const ids$ = this.getIds(originalId);
  // Make http calls for each of the items in the array.
  result = ids$.pipe(switchmap(ids => forkJoin(ids.map(id => this.getId(id))));
  // map the array of ids into an array of Observable<DataType>, forkjoin them and switch into it.

  return result;
}

This is assuming the getIds() call will result in a list of string ids and that you have some getId() function that takes a string ID and returns an observable DataType

bryan60
  • 28,215
  • 4
  • 48
  • 65
  • We ended with the same result except I used the `combineAll()` operator. Is there any disadvantage to my method over a `forkJoin()`? – Buttars Mar 05 '19 at 20:03
  • 1
    Ends up being the same in this use case. The subtle difference is `forkJoin` will only emit 1 value: the final array of results after all the inner observables complete. `combineAll` may emit multiple values: once when each of the inner observables produces a value and again when any of the inner observables produce an additional value. Since your inner observables each terminate after 1 value, this distinction becomes moot. I'd use `forkJoin` though because it seems more semantically correct. – Brandon Mar 05 '19 at 20:34
  • 1
    Same outcome here. Would prefer forkJoin because generally the expectation with combineLatest is that inner streams have multiple emissions whereas forkJoin is declarative that you’re expecting a singe emission. – bryan60 Mar 05 '19 at 20:40
0

You can try this:

ids$.pipe(
  switchMap(ids => //you can swap switchMap with any *Map operator: https://www.learnrxjs.io/operators/transformation/
    forkJoin(...ids.map(id => //you swap forkJoin with any comb. operator: https://www.learnrxjs.io/operators/combination/
      from(Promise.resolve({ id })).pipe(
        map(res => res.id),
        catchError(err => of(err)))))));

Imports for from, forkJoin should be from rxjs, while everything else is imported from rxjs/operators

catchError will catch any thrown unhandled errors.

Demo: https://stackblitz.com/edit/rxjs-xmmhyj

  • 1
    this is a bad use case for zip as it is a non terminating observable and typically in this case you want to use a completing observable (forkJoin) to avoid memory leaks. use of zip may require unneeded subscription cleanup – bryan60 Mar 05 '19 at 18:03
  • forkJoin has a drawback that requires the observable to complete in order to propagate the final result, this can block the stream and you should really consider the trade offs in using the different operators. Every case is different. – Zdravko Tatarski Mar 05 '19 at 18:08
  • 1
    the OP specified these were http calls in question, which means they complete and forkJoin is the correct operator for this use case. – bryan60 Mar 05 '19 at 18:18