Mappers in RxJS
Tags: typescript, angular, rxjs
Published: 2020-07-17 12:24:42

RxJS became one of the main game changers in the world of modern front-end development. Becoming the foundation for multiple technologies and frameworks, it brings us to the necessity of deep knowledge and understanding how it works and how to use it.

In this article I’d like to discuss RxJS mappers. One of the most usual task when we work with data is transformation. Mapper operators become handy when we need to transform every value one by one. 

Let’s start.


## Initial setup
You will need to prepare any environment where you can write Typescript code and see the results. Feel free to do it in any of your favorite way, for example, I prefer to use [Stackblitz](https://stackblitz.com) for my experiments. Signup/in and create empty typescript project.

We need to create the first observable for future use. Let's create it with the help of `interval` function. First, import it:
```typescript
import { interval } from "rxjs";
```

`interval` creates an observable that emits values incrementing by 1 every specified time interval. 

For example:
```typescript
const obs1$ = interval(500)
obs1$.subscribe(console.log);
```
Will print in the console *0, 1, 2, 3, 4, …* every half a second.

If we want to do any modifications of emitted data before subscribing, we must use method `pipe` of observable object. It accepts operator functions as its arguments.

Why do we need, why not to do it in subscriber function? Using `pipe` makes our observables much more reusable and subscriptions cleaner.

Let’s limit our emitted values to 5. To achieve this we need the operator `takeWhile`:
```typescript
const obs1$ = interval(500).pipe(takeWhile(x => x < 5));
```
The code is pretty self-explanatory, it creates observable that emit values until the value is less than 5. Due to autoincrement of the values emitted by `interval` we will get exactly 5 values.


## Map
`map` is the basic transformation operator and we are going to start with it. It looks very familiar from the `Array` type and works the same. As argument it accepts the function that has the current value as it’s argument and must return another value.

Pretty simple:
```typescript
obs1$.pipe(
  map(x => x * 2)
)
.subscribe(console.log);
```
The result will be *0, 2, 4, 6, 8*. Exactly the same like 
```typescript
[0, 1, 2, 3, 4].map(x => x * 2).
```
The difference is that we have this behavior time plane. If you look into console, you will see that you are getting these values not all together, but one by one with interval of 0.5 seconds according to its emitting.

I want to pay your attention on fact that the projection function must **return scalar**. 

What to do if we need to return another observable? For this case, we have `switchMap`.


## Initial setup modification
Let’s apply our new knowledge and modify our `obs1$`:
```typescript
const obs1$ = interval(500).pipe(
  takeWhile(x => x < 5),
  map(x => x + 1)
);
```
Now `obs1$` will emit *1, 2, 3, 4, 5*

Let’s add second observable
```typescript
const obs2$ = interval(200).pipe(
  takeWhile(x => x < 5),
  map(x => x + 10)
);
```
`obs2$` will emit *10, 11, 12, 13, 14*

## SwitchMap
`switchMap` takes every value of observable created pipe and returns the observable returned by its projection function. Let’s make it out on an example.
```typescript
obs1$.pipe(
  switchMap(() => obs2$)
)
.subscribe(console.log);
```
The result will be *10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 12, 13, 14*

Why? This is very important part of understanding how `switchMap` works. So, what’s going on? Every time we have value from `obs1$` our subscription subscribes to `obs2$`. `obs1$` has interval **500** and `obs2$` has interval **200**. It means that we manage to get **2** values of `obs2$` between values of `obs1$`. And when next value of `obs1$` comes it unsubscribes us from `obs2$` and subscribes again. That’s why we have *10, 11* five times and *12, 13, 14* only once when `obs1$` becomes completed.

`switchMap` is very useful when we need to utilize the value of first observable in the second one. For example, we have Angular router that has parameter that we need to send to API to get correct response. This construction is perfect for this task. We can put pipe on `paramMap` or `queryParamMap` and “switch” to new observable that will be returned by `HttpClient`.

But what if we want to get **all** `obs2$` values? For this case, we have `mergeMap`.

## MergeMap
`mergeMap` does exactly the same functionality like `switchMap` except that it doesn’t unsubscribe when new value arrives. Let’s see it:
```typescript
obs1$.pipe(
  mergeMap(() => obs2$)
)
.subscribe(console.log);
```
Now we have *10, 11, 12, 10, 13, 11, 14, 12, 10, 13, 11, 14, 12, 10, 13, 11, 14, 12, 10, 13, 11, 14, 12, 13, 14*

Total 25 values, 5 times 5, all of them.

Good example of using `mergeMap` is [NgRx effects](https://ngrx.io/guide/effects). When we create effect, we `mergeMap` action with the related service method.

The values we get are disordered. We get them in order of emit and when new value from `obs1$` come it starts new cycle of emitting values of `obs2$`. Thanks to this, we get part of values from previous cycle and part from new started. What if the values order matters for us? For this case, we have `concatMap`.

## ConcatMap
`concatMap` works the same way like `mergeMap` except that fact that it emit the values in order. Example:
```typescript
obs1$.pipe(
  concatMap(() => obs2$)
)
.subscribe(console.log);
```

The results will be *10, 11, 12, 13, 14, 10, 11, 12, 13, 14, 10, 11, 12, 13, 14, 10, 11, 12, 13, 14, 10, 11, 12, 13, 14*

Exactly what we wanted.

## Conclusion
Mappers are the most used operators in data manipulations. Very important to understand principles of its work to pick the correct one in your future tasks. When you plan the usage of mappers, first ask yourself *“does my projection function return observable or scalar?”* This can become the starting point to choose the correct operator to use.


<span>Photo by <a href="https://unsplash.com/@nci?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">National Cancer Institute</a> on <a href="https://unsplash.com/s/photos/data-management?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>