Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rxjs - remap object with observables as values

Tags:

I got an observable like this:

  const original = Observable.of({
    a: this.http.get('https://jsonplaceholder.typicode.com/todos/11'),
    b: this.http.get('https://jsonplaceholder.typicode.com/todos/22'),
    c: this.http.get('https://jsonplaceholder.typicode.com/todos/33')
  });

I need to resolve the inner observables and get something like this in the subscribe handler:

  {
    a: ResponseFromServer,
    b: ResponseFromServer,
    c: ResponseFromServer,
  }

How should I approach this problem?

Thanks.

EDIT: I've figured it out, read below.

It seems that not many people know that *Map operators used to have something called resultSelector as their second argument. Now in rxjs v6, you can do the same with inner map, let me show you.

const original = Observable.of({
    a: this.http.get('https://jsonplaceholder.typicode.com/todos/11'),
    b: this.http.get('https://jsonplaceholder.typicode.com/todos/22'),
    c: this.http.get('https://jsonplaceholder.typicode.com/todos/33')
});

const transformed = original.pipe(
    mergeMap(sourceValue => 
        forkJoin(_.values(sourceValue)).pipe(map(resolvedHttpRequests => {
            // here you have access to sourceValue as well as resolvedHttpRequests so you can do manual join to match the data.
        }))
    )
)
like image 704
Patrik Jajo Jajcay Avatar asked Dec 11 '18 16:12

Patrik Jajo Jajcay


2 Answers

Update for 2020

forkJoin(
  // as of RxJS 6.5+ we can use a dictionary of sources
  {
    google: ajax.getJSON('https://api.github.com/users/google'),
    microsoft: ajax.getJSON('https://api.github.com/users/microsoft'),
    users: ajax.getJSON('https://api.github.com/users')
  }
)
  // { google: object, microsoft: object, users: array }
  .subscribe(console.log);

https://www.learnrxjs.io/learn-rxjs/operators/combination/forkjoin

like image 191
Žanas Stundys Avatar answered Sep 19 '22 08:09

Žanas Stundys


If the source object already contains Observables you can do it for example like the following (there are obviously multiple ways to do this):

const mockXHRCall = (url: string) => {
  return of('response');
};

const original = of({
  a: mockXHRCall('https://jsonplaceholder.typicode.com/todos/11'),
  b: mockXHRCall('https://jsonplaceholder.typicode.com/todos/22'),
  c: mockXHRCall('https://jsonplaceholder.typicode.com/todos/33')
}).pipe(
  mergeMap(obj => {
    const observables$ = Object.keys(obj).map(key => obj[key].pipe(
      map(response => (
        { [key]: response } // wrap the response with eg. { c: ResponseFromC }
      )),
    ));

    return merge(...observables$);
  }),
  scan((acc, wrapped) => (
    { ...acc, ...wrapped }
  ), {}),
  takeLast(1),
).subscribe(console.log);

The scan collects all responses and merges them into a single object.

Live demo: https://stackblitz.com/edit/rxjs-ffza8b

like image 22
martin Avatar answered Sep 20 '22 08:09

martin