Use RxJs Pipe to reduce Observable to different type

11,250

Solution 1

So Chau Tran put me on the right lines. Apparently I needed to switchMap the Observable to a Recipe[] and the reduce operator was then happy to accept a Recipe as the value. Solution as follows:

this.foodService.getAllReceipes()
  .pipe(
    switchMap(data => data as Recipe[]),            <<== ADDED THIS

    reduce((array: ChartData[], value: Recipe) => {
        const author = this.createOrFindAuthor(array, value);
        author.y += 1;

        return array;
      }, new Array<ChartData>()),

      switchMap(data => this.chartData$ = of(data.sort((a, b) => b.y - a.y)))
  )
  .subscribe();

Solution 2

After reduce, try:

switchMap(data => {
    this.chartData$ = of(data.sort((a, b) => b.y - a.y));
    return this.chartData$;
})
.subscribe()
Share:
11,250
Simply Ged
Author by

Simply Ged

Software engineer, fighting the good code fight

Updated on July 06, 2022

Comments

  • Simply Ged
    Simply Ged almost 2 years

    I have an Observable<Recipe[]> that I want to reduce to an array of a different class ChartData[] to use as a data source for a highcharts graph (column and piechart).

    I am trying to use the RxJS pipe operator on the Observable<Recipe[]> to call the reduce operator on my data but I cannot get it to work? The reduce operator does not iterate over them items in my Observable<Recipe[]> Below is my attempt:

    this.foodService.getAllReceipes()
      .pipe(
        reduce((array: ChartData[], value: Recipe[], i: number) => {
            const author = this.createOrFindAuthor(array, value[i]);
            author.y += 1;
    
            return array;
          }, new Array<ChartData>())
      )
      .subscribe(data => this.chartData$ = of(data.sort((a, b) => b.y - a.y)));
    }
    

    getAllRecipes() returns the Observable<Recipe[]>

    this.chartData$ is Observable<ChartData[]>

    I am trying to reduce this to ChartData[]. I have been able to do this in the subscribe operator and the graphs display the expected data but I thought I should be able to do it as a pipeable operator? Here is the reduce being done as part of the subscribe:

    this.foodService.getAllReceipes()
      .subscribe((data) => {
        const list = data.reduce((arr: ChartData[], v: Recipe) => {
          const author = this.createOrFindAuthor(arr, v);
          author.y += 1;
    
          return arr;
        }, new Array<ChartData>());
    
        this.chartData$ = of(list.sort((a, b) => b.y - a.y));
      });
    

    I have tried using the subscribe code within the pipeable reduce but I get compile errors saying that the method expects Recipe[] for the value. But if I use the array then I only get the first element from the Observable (or am I just getting the Observable and need to do something with that?)

    Is this possible or is my thought process wrong about how the pipeable operator should work on the Observable?

    For reference here are the models and the createOrFindAuthor function:

    export class Recipe {
    
        public Title: string;
        public Author: string;
        public Source: string;
        public Page: number;
        public Link?: string;
    }
    
    export class ChartData {
        name: string;
        y: number;
    }
    
    private createOrFindAuthor(array: ChartData[], recipe: Recipe): ChartData {
      const name = (recipe.Author.length > 0 ? recipe.Author : 'UNKNOWN');
    
      let found = array.find(i => i.name === name);
    
      if (!found) {
        const newData = new ChartData();
        newData.name = name;
        newData.y = 0;
        array.push(newData);
    
        found = newData;
      }
    
      return found;
    }
    
  • Simply Ged
    Simply Ged almost 6 years
    Although that improves my code it doesn't solve the problem of getting the reduce operator to iterate over all the items in the Observable.
  • Simply Ged
    Simply Ged almost 6 years
    You put me on the right lines, just needed an additional switchMap :-)
  • Chau Tran
    Chau Tran almost 6 years
    Yeah my bad, for some reason I thought you'd get the Recipe[] in reduce already.
  • kctang
    kctang almost 6 years
    I just realized you need "Observable<Recipe[]>" instead of "Observable<Recipe>" as input. Sorry! Anyway, I hope the demo can serve as a guide to tweak from. :-)