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;
}
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();