1

I’ve got an Observable that has an array of objects and I would like to convert them to a different object using a second observable. This is part of a larger project so to simplify this for my question I am going to have an observable that has an array of number and I would like to convert them to a string. I started with the following.

    const response$ = of({results: [1, 2, 3]});
    response$.pipe(
        map((response) => {
            return response.results.map((id) => {
                return id.toString();
            })
        })
    )
    .subscribe((response: string[]) => {
        console.log(response);
    })

The response in the subscribe will be an array of string as expected. Now I need to use a second observable to convert the number to string (again just to make the question simpler). So I replaced return id.toString() with return of(id.toString()) to simulate making a second call to an observable.

    const response$ = of({results: [1, 2, 3]});
    response$.pipe(
        map((response) => {
            return response.results.map((id) => {
                return of(id.toString());
            })
        }),
    )
    .subscribe((response: Observable<string>[]) => {
        
    })

Now the signature of the response is Observable<string>[] but I need the response to be string[] so I started reading about other RxJS operators and I ended up with the following.

    const response$ = of({results: [1, 2, 3]});
    response$.pipe(
        concatMap((response) => {
            return response.results.map((id) => {
                return of(id.toString());
            })
        }),
        concatAll()
    )
    .subscribe((response: string) => {
        console.log('bar', response);
    })

I used concatMap() and concatAll() because I need the call to the second observable to happen in sequence. The problem now is that my response is a string and I get three calls to the subscriber “1” “2” “3”. I need one response of string[]. Can someone explain how to take an Observable<string>[] and convert it to Observable<string[]> in my example?

Eric
  • 343
  • 4
  • 14

1 Answers1

2

I think what you're looking for is this:

const response$ = of({results: [1, 2, 3]});
response$.pipe(
    switchMap((response) => {
        // map array to observables and execute all with forkJoin
        return forkJoin(...response.results.map((id) => {
            return of(id.toString());
        }))
    })
)
.subscribe((response: string) => {
    console.log('bar', response);
})

however, this is going to execute in parallel. if you need sequential execution on the inner, you can use concat and reduce

const response$ = of({results: [1, 2, 3]});
response$.pipe(
    switchMap((response) => {
        // map array to observables and execute all with concat and collect results with reduce
        return concat(...response.results.map((id) => {
            return of(id.toString());
        })).pipe(reduce((acc, v) => acc.concat([v]), []))
    })
)
.subscribe((response: string) => {
    console.log('bar', response);
})

only thing to be careful of is to make sure response.results has items in it. may need a length check like:

if (!response.results.length)
  return of([])
bryan60
  • 28,215
  • 4
  • 48
  • 65
  • Thanks for the fast response! I did look at forkJoin() but it will process in parallel not sequence right? Also its shows depreciated at rxjs6 in VSCode but does not say its deprecated (https://rxjs.dev/api/index/function/forkJoin). I think concat() will run in sequence, right? – Eric Oct 07 '21 at 23:56
  • `forkJoin` is not deprecated. but yes it is parallel execution, i added what you need to do for seequential. – bryan60 Oct 07 '21 at 23:57
  • that is really nice. Can you explain what the three dots (...) does in the concat() method? I am familiar with the spread operator but I think this is something different. – Eric Oct 08 '21 at 00:15
  • It’s just converting the array to arguments to the function. I’m honestly not sure if it needs to be there anymore since RXJs has changed their interfaces a few times and it’s more flexible now but it used to be needed and I do it out of habit. – bryan60 Oct 08 '21 at 00:28
  • Well I tried it with out the three dots and it returns Observable[]. – Eric Oct 08 '21 at 00:30