Reputation: 23
What I'm trying to achieve is to make multiple requests to an api, merge and sort the result and assign it to the this.products$ observable. This mostly works as the code looks now (see below) in that the end result finally turns into a single list.
The fetchIds()-method get the the parameters from a store. sliceToArrays(any[], number) take in the parameter list and split it into smaller parts. The reason I'm doing this is to avoid making the url too long for the GET method this.api.listProducts([],''), since an url query has a maximum character limit that gets hit when there are too many Ids.
The problem I'm facing, except for the gnawing feeling that I'm using this the wrong way, is that I can't get it to sort the merged result; it sorts 2 lists, before combining them, making it look like this [A, B, C, A, B, C] instead of [A, A, B, B, C, C].
ngOnInit() {
this.pageSize = 100;
this.products$ = this.fetchIds().pipe(
mergeMap(ids =>
forkJoin(this.sliceToArrays(ids, this.pageSize).map(idsList => this.api.listProducts(idsList, 'watchlist'))).pipe(
mergeMap(products => merge(...products)),
toArray(),
map(products => sortBy(products, ['inventoryStatus', 'authorLastFirst']))
))); }
sliceToArrays(input: any[], maxSliceSize: number): any[][] {
const slices = Math.floor((input.length + maxSliceSize - 1) / maxSliceSize);
const collection: any[][] = [];
for (let i = 1; i <= slices; i++) {
collection.push(input.slice((i - 1) * maxSliceSize, i * maxSliceSize));
}
return collection; }
fetchSkus() {
return this.watchListStore.items.pipe(
filter(watchlist => watchlist !== null),
map(watchlist => watchlist.map(i => i.id))
); }
Anyone who could point me in the right direction? I've been trying different things for days, but this (angular, typescript, etc) isn't really my area of expertise.
Upvotes: 2
Views: 944
Reputation: 1062
I can't get it to sort the merged result; it sorts 2 lists, before combining them, making it look like this [A, B, C, A, B, C] instead of [A, A, B, B, C, C].
I think you can flatten your stream a little bit. Also, don't forget the error handling. Maybe something like this:
this.products$ = this.fetchids().pipe(
map(ids => this.sliceToArrays(ids, this.pageSize)),
switchMap(idLists => combineLatest(...idsList.map(ids => this.api.listProducts(ids, 'watchlist')))),
map(productLists => productLists.reduce((acc, curr) => ([...acc, ...curr]), [])),
map(products => products.sort((a, b) => a.inventoryStatus.localCompare(b.inventoryStatus))),
catchError(error => of([])) // and a nice message to the UI
)
When the logic is non-trivial like here, I like to make a human-readable layer, and hide the hacky logic in functions:
this.products$ = this.fetchids().pipe(
map(ids => splitIdsIntoQueriableChunks(ids, this.pageSize)),
switchMap(idLists => fetchAllProducts(idLists)),
map(productLists => mergeProducts(productLists)),
map(products => sortProducts(products)),
catchError(error => notifyError(error))
)
Upvotes: 1