Home > Net >  How do I combine two data streams, filter them based on an objects field, and then merge them in RxJ
How do I combine two data streams, filter them based on an objects field, and then merge them in RxJ

Time:08-06

I am getting a little overwhelmed with combining data streams using declarative RxJS and I haven't been able to figure this one out.

I have two data streams like so:

seriesCards$ = this.http.get<SeriesCard[]>(this.seriesCardsUrl)
    .pipe(
      shareReplay(1),
      catchError(CardsService.handleError)
    );

and,

allWatchCards$ = this.http.get<AllWatchCards[]>(this.watchCardsUrl)
    .pipe(
      shareReplay(1),
      catchError(CardsService.handleError)
    );

As you can see, each one returns a different object array. I have a requirement in my app that now I need to combine them to return them all together based on a certain criteria for each one.

Each object has a field called category and that's the one I want to filter on for both.

Ideally I want my service to combine both the data streams and then my page/component to do the filtering and return each object from both object arrays based on that filter.

My service.ts:

watchCardWithSeriesCardsForDevotionals$ = combineLatest([
    this.allWatchCards$,
    this.seriesCards$
  ]).pipe(
    mergeMap(([watchCards, seriesCards]) =>
      merge(watchCards, seriesCards),
    ));

My component.ts

displayBothCards$ = this.cardService.watchCardWithSeriesCardsForDevotionals$
    .pipe(
      map(bothCards => bothCards) <!-- This is where I want to filter but it defaults to only the one objects field? -->
    );

For a reason I am not sure of, the map(bothCards => bothCards) is flattened to an object instead of an array of those objects so.

An example from a single data stream that I have that I am trying to do with two data streams:

displaySomething$ = this.cardService.allWatchCards$
    .pipe(
      map(somethingCards => somethingCards.filter(somethingCard => somethingCard.category === 'something')
        .sort(sortByDate))
    );

Any help and guidance would be appreciated!

CodePudding user response:

You can use forkJoin:

Service

watchCardWithSeriesCardsForDevotionals$ = forkJoin({
  watchCards: this.allWatchCards$,
  seriesCards: this.seriesCards$
});

Component

displayBothCards$ = this.cardService.watchCardWithSeriesCardsForDevotionals$
 .pipe(
   tap(({ watchCards, seriesCards }) => {
     console.log(watchCards);
     console.log(seriesCards);
   }),
   map(({ watchCards, seriesCards }) => {
     const filteredWatchCards = watchCards.filter((watchCard) => watchCard.category === 'devotional');
     const filteredSeriesCards = seriesCards.filter((seriesCard) => seriesCard.category === 'devotional');
      
    return { 
      myFilterWatchCards: filteredWatchCards as SeriesCards[]
      myFilterSeriesCards: filteredSeriesCards as AllWatchCards[]
    };
   })
 );
<ng-container *ngIf="displayBothCards$ | async as bothCards">
 <div *ngFor="let watch of bothCards.myFilterWatchCards">
   <!-- watch.something -->
  </div>

 <div *ngFor="let serie of bothCards.myFilterSeriesCards">
   <!-- serie.something -->
  </div>
</ng-container>

You can read more about it here

CodePudding user response:

It's because you are using the wrong operators. Try this instead:

watchCardWithSeriesCardsForDevotionals$ = combineLatest([
    this.allWatchCards$,
    this.seriesCards$
]).pipe(
    map(([watchCards, seriesCards]) => [...watchCards, ...seriesCards])
);

BTW, the reason your array is flattened is because of the mergeMap.

  • Related