Map versup FlatMap

flatMap is used to flatten an array of arrays into a single array.

map simply converts one array to an other array. For example, suppose you have a list of person objects like this:

const friends = [
{name: 'Dave', kids: ['Max', 'Jack']},
{name: 'Max', kids: ['Sam', 'Alex', 'Megan']},
{name: 'Jordan', kids: ['Mason', 'Cameron', 'Kaylin']}
];

But what you really need is an array of person names (i.e. strings: [“Dave”, “Max”, “Jordan”]). To convert this array of person object to array of strings, you would first define your mapping function like this:

const mapFunction = p -> p.name;

Then, use array.map like this:

const names = friends.map(mapFunction);

which returns:

["Dave", "Max", "Jordan"]

flatMap is similar to map in that you are converting one array into another array. But there are a few subtle differences:

First of all, map is generally a one-to-one thing. The mapping function takes one object in and returns one object out:

p -> p.name

This means that 3 person objects in will produce 3 names out.

flatMap, on the other hand, is a one-to-many thing. The mapping function takes one object in but returns an array out:

p -> p.kids

The net result: 3 person objects in will produce 8 kid names out. Thus, this code:

const mapFunction = p -> p.kids;
const kidNames = friends.flatMap(mapFunction);

will return:

["Max", "Jack", "Sam", "Alex", "Megan", "Mason", "Cameron", "Kaylin"]

To summarize:

Problem: JavaScript’ doesn’t actually have a flatMap method

JavaScript doesn’t currently have a flatMap function built-in. Here is how to add flatMap to array’s prototype:

Array.prototype.flatMap = function(lambda) {
return Array.prototype.concat.apply([], this.map(lambda));
};

RxJs and flatMap

RxJs does have a flatMap function. The principle is similar. A flatMap is used to flatten an observable of observables into a single observable.

For RxJs, the mapping function looks like this:

item => Observable

rather than this:

item => Array

One thing that can be confusing is that RxJs, like SQL, uses the same data structure to represent one thing or many things. So it is quite common for an observable to represent a collection of just one thing.

Here is an example that might seem tricky at first:

http.get('/api/person/123')
.map(res => res.json())
.flatMap(person => http.get('/api/dealer/' + person.zipCode))
.map(res => res.json())
.subscribe(dealer => {
console.log(dealer);
});

First of all, it may seem odd using map and flatMap for collections of just one thing. The person http call returns a single person. And the dealer http call returns a single dealer. So in this case, you can think of the map call to simply mean transform. For example, the line:

.map(res => res.json())

just means: transform the response body’s JSON text into a javascript object.

Secondly, you may be wondering why we need the flatMap rather than map. Here is why: If the dealer http call returned a simple dealer object then map would work just fine. But it doesn’t return a dealer. It returns an observable of dealer. So if you used map, you would end up with an observable containing an observable. So in this use-case, flatMap is really just used to unwrap the dealer.