Search code examples
angularrxjsrxjs6

Rxjs - Comparing .map() with map()


Compare this

return this.httpClient.get('https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/suggest', {params: data}).pipe(
            map((x: any) => x.suggestions.map((y) => {
                return {
                    ...y,
                    title: y.text
                };
            })),
        );

with this

return this.httpClient.get('https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/suggest', {params: data}).pipe(
            map((x: any) => x.suggestions),
            map((y: any) => y.title = y.text)
        );

Why these cases are not equal?

The first work as expected, "remove" suggestions property and duplicate property text to title

The second returns me a value... not array

Can I use only map() function instead of .map()?


Solution

  • First one is RxJS's pipeable operator map and second one is Array's map function.

    Let's have a look at types in your example:

    In the first example, you get Observable of object. In this case map operator allows you to transform your object with suggestions array inside of it. Then via Array's map function you map each item from x.suggestions array to new item.

    const obs$: Observable<{ suggestions: any[] }> = this.httpClient.get('url', { params: data });
    obs$.pipe(
      map((x: { suggestions: any[] }) => x.suggestions.map((y) => {
        return {
          ...y,
          title: y.text
        };
      })),
    );
    

    In the second example after first map operator, you get observable of array. Thus second map operator has an error.

    const obs$: Observable<{ suggestions: any[] }> = this.httpClient.get('url', { params: data });
    const tmp$: Observable<any[]> = obs$.pipe(
      map((x: { suggestions: any[] }) => x.suggestions),
    );
    tmp$.pipe(
      map((x: any[]) => x.title = x.text) // this line has error: Property 'title' does not exist on type 'any[]'.
    )
    

    Since you have Observable of array, you are mapping each item, which is array emitted from the Observable, to the new item.

    In case of Array, you are mapping each item, which is object from array, to the new item.

    Your thinking was that in the Observable of Array, it would allow you to map items from the array to new items, but it's not the case. It allows you to map the whole Array to whatever you want.

    As a solution to what you wanted to do, have a look at this:

    this.httpClient.get('url', { params: data })
      .pipe(
        switchMap(x => x.suggestions),
        map(x => (...x, title: x.text)),
        toArray(),
      )
    

    Now, via switchMap, you get Observable of items (instead of Observable of array) and you can map each item as you wish, then transform it back to Observable of array via toArray operator.