Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use RxJs Pipe to reduce Observable to different type

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;
}
like image 479
Simply Ged Avatar asked May 03 '18 12:05

Simply Ged


People also ask

What does pipe () do RxJS?

RxJS' pipe() is both a standalone function and a method on the Observable interface that can be used to combine multiple RxJS operators to compose asynchronous operations. The pipe() function takes one or more operators and returns an RxJS Observable.

Does pipe subscribe to Observable?

The angular async pipe 'allows the subscription to observe value of anything inside of the angular template syntax'. It also takes care of unsubscribing from observables automatically.

How do you use the pipe on observables?

Using pipe to combine operators Each argument must be separated by a comma. The order of the operators is important because when a user subscribes to an observable, the pipe executes the operators in a sequence in which they are added. To use observable we need it to import from the rxjs library.

Does pipe return a new Observable?

pipe(operator()) . These include, filter(...) , and mergeMap(...) . When called, they do not change the existing Observable instance. Instead, they return a new Observable, whose subscription logic is based on the first Observable.


2 Answers

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();
like image 87
Simply Ged Avatar answered Nov 15 '22 03:11

Simply Ged


After reduce, try:

switchMap(data => {
    this.chartData$ = of(data.sort((a, b) => b.y - a.y));
    return this.chartData$;
})
.subscribe()
like image 37
Chau Tran Avatar answered Nov 15 '22 05:11

Chau Tran