angular - Utföra flera Http-förfrågningar till samma slutpunkt, slå samman och sortera svar


Det jag försöker uppnå är att göra flera förfrågningar till ett API, slå samman och sortera resultatet och tilldela det till this.products$-observabeln. Detta fungerar mestadels som koden ser ut nu (se nedan) eftersom slutresultatet till slut blir en enda lista.

fetchIds()-metoden hämtar parametrarna från en lagring. sliceToArrays(any[], number) tar in parameterlistan och delar upp den i mindre delar. Anledningen till att jag gör detta är för att undvika att göra URL:en för lång för GET-metoden this.api.listProducts([], ''), eftersom en URL-fråga har en maximal teckengräns som överskrids när det finns för många Ids.

Problemet jag står inför, förutom den gnagande känslan av att jag använder detta på fel sätt, är att jag inte kan få det att sortera det sammanslagna resultatet; det sorterar två listor innan de kombineras, vilket gör att det ser ut så här [A, B, C, A, B, C] istället för [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))
); }

Finns det någon som kan peka mig i rätt riktning? Jag har provat olika saker i flera dagar, men det här (Angular, TypeScript, etc) är egentligen inte mitt expertområde.

Svar #1

Jag kan inte få det att sortera det sammanslagna resultatet; det sorterar 2 listor innan de kombineras, vilket gör att det ser ut så här [A, B, C, A, B, C] istället för [A, A, B, B, C, C].

Jag tror att du kan jämna ut din ström lite. Glöm inte heller felhanteringen. Kanske något liknande detta:

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
)

När logiken är icke-trivial som här, gillar jag att skapa ett mänskligt läsbart lager och gömma den hackiga logiken i funktioner:

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))
)