Tomas Hjelm
Tomas Hjelm

Reputation: 23

Performing multiple Http requests to same endpoint, merge and sort response

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

Answers (1)

D&#225;niel Barta
D&#225;niel Barta

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

Related Questions