Get started transforming streams with map, pluck, and mapTo
When working with observables one of the most common use cases you will encounter is the need to transform a stream of some value type into a stream of another value type. For instance, you may have an observable of click events that you wish to transform into an observable of objects containing just the clientX
and clientY
coordinates. Or maybe you need to extract a value from a stream of input events to perform a calculation or initiate a request. Or perhaps you just want to extract a single property from an object, like a key code, to perform another action down the (pipe)line. The scenarios for transforming streams are endless.
In this article, we are going to learn about the most common operator used to transform streams, the map
operator. We will start by taking a look at Array.map
to build a general understanding of the map
operation. Next, we will explore how we can apply this approach to observables by using the RxJS map
operator. Finally, we will check out a few helper operators that can be used in place of map
should the right scenario present itself, exploring common use cases along the way. Let's get started!
Introducing map
map
If you have spent time working with JavaScript arrays you may already be familiar with Array.map
. When dealing with arrays, the map
method lets you transform an array by applying a provided function (often referred to as a 'projection' function) to each item within the array. For instance, let's say we have an array of numbers 1-5
:
If we wanted to transform this into an array of each number multiplied by ten, we could use the map
method. To do this, we call map
on our numbers array, passing it a function which will be invoked with each value of the source array, returning the number multiplied by ten:
The map
method does not mutate the existing array, but instead returns a new array. For example, if we were to log the numbers
array after calling map
, we can see that it's unchanged:
To understand this better, let's walk through what a naive implementation of Array.map
could look like.
We create a new array.
For every item contained in the source array we apply the provided function.
We then push the result of this function to a temporary
resultArray
.After doing this for every item, we return the new array.
While the real implementation of Array.map
includes features like index tracking and proper error management, this gives us a general sense of how things work behind the scenes.
So what are some other common scenarios where we could put the map
method to use? Using Array.map
, we may also want to transform objects. For instance, suppose we have an array of objects with a first and last name property and we want to tack on a full name property to each object. We could accomplish this by supplying a function that accepts each object and maps it to a new object that includes all current properties plus the new fullName
property. In this example we are using the object spread syntax and template literals, but you could also explicitly rewrite the properties:
Another common use case for map
is extracting a single property from an object. For example, given the sample above suppose we decided we only really need the last name property for display. Instead of our function returning a new object, we can instead return just the property we need from that object:
At this point, we are transforming an array of people objects into an array of string last names.
As you can see, the map
method is extremely flexible with a wide variety of use cases, but how does this translate to map
with RxJS, and when would you put this to use with observables?
Introducing the RxJS map
operator
map
operatorThe map
operator in RxJS transforms values emitted from the source observable based on a provided projection function. This is similar to Array.map
, except we are operating on each value emitted from an observable as it occurs rather than each value contained within an array.
For instance, let's start with our initial example, but instead of transforming an array of numbers let's transform an observable of numbers. To do this, we will use the from
creation operator to first convert our numbers array into an observable:
When provided an array, the from
creation operator will loop through (synchronously) emitting each item in sequence. When we subscribe we can see each value printed to the console:
Tip: If you want to see how from
handles each value type behind the scenes, you can check out the subscribeTo
, and associated helper functions. In this case, subscribeToArray is used. This same helper function is also used to deal with non-observable return values of flattening operators, such as mergeMap
If we then wanted to transform this observable into the emitted values multiplied by ten, we could use the map
operator. Just like Array.map
, the map
operator accepts a project function which describes how each value from the source will be transformed. In this case, we will provide a function that accepts the emitted value from the source observable and returns that value multipled:
Instead of the function being applied to each item of an array, before a new array is returned, with observables the project function is applied and the result emitted in real-time as values blast through your streams. We can confirm this in the RxJS source code by seeing the function we provide is invoked, with the result being passed on to the subscriber (destination):
Similar to our array example with objects, we may also want to transform an observable of objects with the map
operator. For instance, suppose we have an observable of click
events that we wish to transform into an observable of objects containing just the clientX
and clientY
coordinates of these events. To do this we could apply the map
operator, providing a function that returns an object with just these properties:
There may also be times we want to grab a single property from an object using map
. For example, we may have a use case for an observable of just the code
property from keyup
events, so we can take action when the user types a particular character or key. To do this we can apply the map
operator returning just the property we are interested in:
While map
works perfectly fine in these situations, RxJS also surfaces helper operators for cases where you just want to map to a single property or when you always want to map to the same value on any event. First, let's take a look at the single property scenario.
Extract a single property with pluck
pluck
RxJS features many operators that are simply shortcuts for other operators. For example, any time we just want to grab a single property from an emitted value, instead of using map
we could use pluck
. The pluck
operator accepts a list of values which describe the property you wish to grab from the emitted item. For instance, using our event code example from above we could use pluck
instead of map
to extract the code
property from the event
object:
We can also pass pluck
multiple values to grab a nested property within an object. For example, if we wanted to grab the nodeName
from the target
element on click, we could pass both of these properties to pluck
in order:
Like many other helper operators in RxJS, behind the scenes pluck
is simply reusing the map
operator, passing it a function to grab the appropriate property:
Functionally, map
and pluck
will operate the same in these scenarios, I would suggest using whichever you feel most comfortable reading at a glance.
Lastly, there may also be times where you always want to map to a single value, no matter the input. For these situations, you can use the mapTo
operator.
Mapping to a constant value with mapTo
mapTo
For situations where you find yourself always wanting to map to a specific value, one way you could handle it is by simply using map
and ignoring the input:
While this works, the wrapping function isn't necessary since we are ignoring the received value. For these scenarios you can replace map
with mapTo
, and simply provide the value you wish to return on all emissions:
Like pluck
, mapTo
provides no real benefit functionally over returning a constant value with map
, but syntactically it may prove slightly easier to consume and read at a glance.
Conclusion
In conclusion, map
is a versatile operator which lets you transform a stream using a provided projection function. Whether it's mapping to a keycode, value updates from an input box, or reshaping an object, map
will be one of the most used operators in your day-to-day RxJS toolbox. For scenarios where you just need to map to a single property, or always want to map to a constant value, you can also check out the pluck
and mapTo
helper operators.
For a full list of transformation operators with examples, including operators which manage mapping to more complex values such as other observables, check out the transformation operator section. We will explore these topics in detail in future posts!
Last updated