Henrique Erzinger
Henrique Erzinger

Reputation: 1147

Push value from Observable<Object> into Observable<Array>

I have two different API endpoints. One returns a Cards object, the other an Array of Cards objects. What I want to do is get the first Card as the first element in the Array from the second endpoint. Is there any way to do that? Both are observables returned from HTTPClient, so maybe it would be simple to do with some operator, but I don't know enough to do that yet. Just to illustrate it better:

I have:

latestCards$: Observable<Cards> = http.get('latestCardsEndpoint');
// { title: 'Latest', cards: [], ... }  

featuredCards$: Observable<Cards[]> = http.get('featuredCardsEndpoint');
// [
//   { title: 'Featured1', cards: [], ... }, 
//   { title: 'Featured2', cards: [], ... },
//   ...
// ]

I need

homeCards$: Observable<Cards[]>;
// [
//   { title: 'Latest', cards: [], ... },
//   { title: 'Featured1', cards: [], ... },
//   { title: 'Featured2', cards: [], ... },
//   ...
// ]

Upvotes: 1

Views: 3156

Answers (2)

Jota.Toledo
Jota.Toledo

Reputation: 28434

Sure:

Naive approach:

import {forkJoin} from 'rxjs/observable/forkJoin';
import {map} from 'rxjs/operators';

homeCards$= forkJoin(latestCards$,featuredCards$).pipe(
  map(([latest,feature]=>[latest,...feature])
);

This has one limitation: waits for both requests to be completed to emit the first value, which is the complete list.

Naive approach 2:

import {combineLatest} from 'rxjs/observable/combineLatest';
import {map} from 'rxjs/operators';

homeCards$= combineLatest(latestCards$,featuredCards$).pipe(
  map(([latest,feature]=>[latest,...feature])
);

Same limitation as before, only that it waits for both streams to emit at least once. Because these are http observables, they only emit once (response) and then complete. So its the same.

A better approach:

Assuming that the latestCard$ request takes less time to respond, use it as first result while fetching the rest of the data on the background.

import {merge} from 'rxjs/observable/merge';
import {of} from 'rxjs/observable/of';
import {mergeMap, map}from'rxjs/operators';

homeCards$= latestCards$.mergeMap(latest => {
            const fullResult$ = featuredCards$.map(featured => [latest,...featured]);
            return merge(of([latest]), fullResult$);
            });

Upvotes: 2

Boris Lobanov
Boris Lobanov

Reputation: 2454

There are many ways to solve this, one would be this:

Observable
    .combineLatest(latestCards$, featuredCards$) // return an array of responses
    .map(([lastestCard, featuredCards]) => [lastestCard, ...featuredCards])
    .subscribe(// do your stuff)

Here's an example in jsfiddle: http://jsfiddle.net/foxfghhn/

Upvotes: 2

Related Questions