1

I have an API (getNewStories) that returns the data as an array of numbers(ids) such as [1,2,3,4...]. There is another API (getItem) that uses the number(id) and give its details.

How can I accomplish this with rxjs operators, so that I should only subscribe to it once and it gives me an array of the records with those ids?

I am able to accomplish this using 2 subscriptions, but I want it with one. Is it possible? and if it's, then, how?

this.hnService.getNewStories().subscribe(data => {
  // data is [1,2,3,4,5]
  // create an array of observables for all the ids and get the record for that id
  const observables = data.map(item => this.hnService.getItem(item));
  // use forkJoin to combine the array to single results variable
  forkJoin(...observables).subscribe(results => {
    this.stories = results;
  });
});

with this I have to subscribe to both the APIs.

GMachado
  • 791
  • 10
  • 24
Satpal Tanan
  • 1,108
  • 7
  • 17
  • 2
    If you ever find yourself doing `.subscribe(() => { ....subscribe();});` in rxjs you know your going wrong. There should only ever be one subscribe to each subscription – Liam Aug 11 '20 at 14:01
  • I agree with you, that's why the question is here – Satpal Tanan Aug 11 '20 at 16:19

3 Answers3

2

You were going the right direction with using forkJoin:

this.hnService.getNewStories()
  .pipe(
    concatMap(data => {
      const items$ = data.map(item => this.hnService.getItem(item));
      return forkJoin(...items$);
    }),
  )
  .subscribe(allItems => ...);

forkJoin will wait until all source Observables complete and only then emit all results as a single array.

martin
  • 93,354
  • 25
  • 191
  • 226
0

I think you can achieve this using a flattening operator like this.

this.hnService.getNewStories().pipe(
            .mergeMap(data => this.hnService.getItem(data))
            .subscribe(res => this.stories = res);

Other option can be to create two observable streams and use a combineLatest.

gourav
  • 94
  • 1
  • 6
0

You can simply have implemented as in the the following snippet: (Yes, mergeAll flattens an observable containing an array, for further explanation refer to @Martin's post about the Best way to “flatten” an array inside an RxJS Observable )

getNewStories().pipe(mergeAll(), concatMap(this.getItem), toArray()).subscribe()

You can try running the following snippet:

const { of } = rxjs;
const { concatMap, toArray, mergeAll  } = rxjs.operators;


function getItem(x) {
  return of({ item : x })
}

of([1, 2, 3, 4])
  .pipe(
    mergeAll(),
    concatMap(getItem),
    toArray()
  )
  .subscribe(console.log)
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.6.2/rxjs.umd.min.js"></script>
Rafi Henig
  • 5,950
  • 2
  • 16
  • 36