简体   繁体   中英

Using MergeMap with the array of data received from another observable - RxJs Angular

I have a fetchDrives method which will return an observable which on subscription will return a list of drives

this.fetchDrives(points).subscribe(drives => {
     console.log(drives);
});

Assume The drives array which I got on subscription look some what like this

[ {driveId: 1}, {driveId: 2}, {driveId: 3} ]

Now I need to use the driveId one by one and make three calls ( three because length of the drives array is 3 ) by passing driveId to each api call.I need to pass driveId to the below method one at a time and get the lat and lon and store the result of three calls in an array.

this.getLatLong(driveId).subscribe( res => console.log(res))

The res will contain an object like { lat: 12, lon: 54 }

I don't want to do two subscriptions, is there a way I can use the Rxjs operators and achieve this with one subscription using the result of previous observable, loop through the drives array and make three calls to getLatLong method using mergeMap as the sequence of the calls doesn't matter and store the result of those three calls in an array?

I tried using scan operator to loop through but failed to use it to get the desired output

Thanks for the help in advance:)

If I understood your question correctly, you want to flatten drives array in order to iterate each drive, and then make API call to get each drive lat lon.

You can achieve such thing with mergeAll and mergeMap operators.
Your code should look something like this:

fetchDrives().pipe(
  mergeAll(), // Flatten the drives array
  mergeMap(drive => getLatLong(drive)) // Get each drive { lat, lon }
).subscribe(console.log)

You can run the full example in this stackblitz

If the order of the requests doesn't matter, you could use RxJS forkJoin method to make the calls simultaneously. I've also used switchMap operator to switch the observable once the source observable ( this.fetchDrives(points) ) emits. Try the following

locations: any;

this.fetchDrives(points).pipe(
  switchMap((drives) => {
    let source = Object.create(null);
    for (let i = 0; i < drives.length; i++) {
      source[drives[i]['driveId']] = this.getLatLong(drives[i]['driveId']);
    }
    return forkJoin(source);
  })
).subscribe(
  response => {
    this.locations = response;
  },
  error => {
    // handle error
  }
);

Variable locations will be of the form

// 'driveId': { lat: 12, lon: 54 }

{
  '1': { lat: 12, lon: 54 },
  '2': { lat: 12, lon: 54 },
  '3': { lat: 12, lon: 54 }
}

Update: array of objects output

To return an array of objects from forkJoin you could send in an array of observables as the argument. For eg. forkJoin([obs1, obs2, ...]) . In the previous case we were sending an object as the argument ( forkJoin({'name1': obs1, 'name2': obs2, ...}) , so the output will also be an object.

locations: any = [];

this.fetchDrives(points).pipe(
  switchMap((drives) => {
    return forkJoin(drives.map(drive => this.getLatLong(drive['driveId'])));
  })
).subscribe(
  response => {
    this.locations = response;
  },
  error => {
    // handle error
  }
);

locations will be of the form

[
  { lat: 12, lon: 54 },
  { lat: 12, lon: 54 },
  { lat: 12, lon: 54 }
]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM