Reputation: 320
I need to flat the results of a Observable<Order[]>[] in a Order[].
The current way that I'm doing it:
const ordersObservable: Observable<Order[]>[] = [];
//ordersObservable is populated with a bunch of Observable<Order[]>
forkJoin(ordersObservable)
.pipe(
map((results) => ([] as Order[]).concat(...results))
)
.subscribe((orders: Order[]) => {
this.orderService.set(orders);
//...
});
I've been told that I shouldn't be using pipe
like that and should be using a RxJs function to handle that.
I've tried to use concatAll
and mergeAll
instead of the map
, but that ended in calling the orderService
for every item in ordersObservable
, instead a single time with a flat array with all results of ordersObservable
.
What am I doing wrong? And how can I flat the results of all the observables in a single array, preferably with a native RxJs solution?
Upvotes: 0
Views: 220
Reputation: 60518
An Observable is meant to emit values. With an array of Observables, we need to subscribe to each element of the Observable array to emit the Order array.
I honestly think you should re-evaluate creating an array of Observables. If you want to post another question with the code that is generating that array of Observables, we could provide suggestions.
That said, I was able to get something to work. I didn't spend the time to see if there was an easier approach.
from(this.ordersObservable)
.pipe(
concatAll(),
scan((acc, value) => [...acc, ...value], [] as Order[]),
takeLast(1)
)
.subscribe((x) => console.log('result', JSON.stringify(x)));
First, I needed something to subscribe to. We can't subscribe to the array of Observables. So I used from
to turn the Array of Observables into another Observable. That way I could subscribe and get the code to execute.
The from
emits each Observable from the array.
NOTE: forkJoin
and combineLatest
also work instead of from
and provide the same result.
I then use concatAll()
to concatenate the inner Observables in sequence. It subscribes to each Observable in the array.
scan
allows us to define an accumulator, accumulating each array of Orders into a single array of orders.
UPDATE: Added takeLast(1)
to ensure only the last result is emitted.
UPDATE 2: Second option
This also worked and may be a bit simpler:
concat(...this.ordersObservable)
.pipe(
scan((acc, value) => [...acc, ...value], [] as Order[]),
takeLast(1)
)
.subscribe((x) => console.log('result', JSON.stringify(x)));
This uses array destructuring (the "...") with the concat
operator to emit all of the values from all of the Observables in the array of Observables. Then we still use scan
to accumulate them and takeLast(1)
to emit only once.
I would be interested to know if anyone else is able to simplify this!
Upvotes: 1