Using flatMap in ES2019
Since it’s already April, looks like it’s time to start talking about the new JavaScript standard, ES2019. This new JS standard includes a few interesting extras but nothing too dramatic. The most interesting new addition is called flatMap, and though it may not be revolutionary, it is quite useful in many situations.
So what does it do? Before we get into flatMap, let’s start by looking at map, an array method in JavaScript. Alright then, so what does map do? Glad you asked. We use it when we want to go over all the members of an array and return an array of the same size with mutation (when we change the members of the array). For instance:
const myArray = ['Ran', 'Moshe', 'Yaakov', 'Yossi'];
const result = myArray.map((name) => {
return `${name} is nice!`
});
console.log(result);
//["Ran is nice!", "Moshe is nice!", "Yaakov is nice!", "Yossi is nice!"]
What do we have here? An array of names upon which I’m running the map function. This function is found in all arrays and takes an anonymous function (in our case, an arrow function) which returns the name after a simple mutation that adds text to it.
But if we try and create complicated mutations, we enter into a whole new world of pain. Imagine a recipe website. I choose a recipe and its ingredients, which are stored in an array. I want to make a feature that, when a user clicks a button, the ingredients are added to a shopping list. If the user chooses two recipes, I’ll have in my array two members. The first will have the ingredients from the first recipe and the second member will have those of the second recipe:
['Carrot,Cabbage,Salt,Mayonnaise', 'Bread,Oil,Eggs,Cream,Sugar']
But what I want is something that looks like this:
["Carrot", "Cabbage", "Salt", "Mayonnaise", "Bread", "Oil", "Eggs", "Cream", "Sugar"]
How could I do this? The first thing that pops to mind is map. We could go over the entire array and then run split to separate the ingredients. Ah, but there’s a catch. This would result in two arrays. The map function maps exactly to the original amount of members. Had two members to start with? Then you’ll have two members when you’re done.
const myArray = ['Carrot,Cabbage,Salt,Mayonnaise', 'Bread,Oil,Eggs,Cream,Sugar'];
const result = myArray.map((name) => {
return name.split(',');
});
console.log(result);
//[["Carrot Cabbage Salt Mayonnaise"], ["Bread Oil Eggs Cream Sugar"]]
And that’s pretty disappointing since I want one array that has all of the ingredients. So what can we do? Well, we could run flat on the array, or better yet… flatMap! Check it:
const myArray = ['Carrot,Cabbage,Salt,Mayonnaise', 'Bread,Oil,Eggs,Cream,Sugar'];
const result = myArray.flatMap((name) => {
return name.split(',');
});
console.log(result);
//["Carrot", "Cabbage", "Salt", "Mayonnaise", "Bread", "Oil", "Eggs", "Cream", "Sugar"]
Very elegant indeed. It saves us from having to use flat. Since flatMap doesn’t have to return the same number of members, we can use it both for mutation and filter. So let’s pretend I’m a disgruntled IT employee who’s been tasked with making an algorithm of responses that takes an array of ages. For everyone under the age of 35, we write that they’re OK for employment in high-tech, and everyone 35 and up we kick to the curb. How does our cranky employee do this?
If he tries to use map, he’ll get some unexpected results:
const highTechWorkers = ['20', '25', '41', '9'];
const result = highTechWorkers.map((worker) => {
const age = parseInt(worker);
if (age < 35)
return `age ${worker} is OK`;
});
console.log(result);
//["age 20 is OK", "age 25 is OK", undefined, "age 9 is OK"]
Why? Because map returns the exact same values as the original array. If I want to filter, I need to run the filter function and then run map:
const highTechWorkers = ['20', '25', '41', '9'];
highTechWorkersFiltered = highTechWorkers.filter((worker) => {
const age = parseInt(worker);
if (age < 35) {
return true;
} else {
return false;
}
});
const result = highTechWorkersFiltered.map((worker) => {
return `age ${worker} is OK`;
});
console.log(result);
//["age 20 is OK", "age 25 is OK", "age 9 is OK"]
This can be done much more elegantly of course, but still you’d be using filter and map. But flatMap can save me the whole headache since it doesn’t have to return the same members. Plus, as opposed to filter, flatMap executes mutation in the array. How? Anything I want, I’ll return to a single array and anything I don’t want to an empty one. The flatMap takes what I made, gets rid of all the empty arrays, and returns the full ones in an array. Fun as fun can be:
const highTechWorkers = ['20', '25', '41', '9'];
const result = highTechWorkers.flatMap((worker) => {
const age = parseInt(worker);
if (age < 35) {
return [`age ${worker} is OK`]; // Keep it, in array
} else {
return []; // Delete it!
}
});
console.log(result);
//["age 20 is OK", "age 25 is OK", "age 9 is OK"]
If you’re thinking this sounds a bit complicated for you, just play around with it in the CodePen above. But make sure you keep this in mind: Do you need to mutate the array and filter it too? Instead of using filter and then map or another alternative like reduce, consider using flatMap. Again, it may not be a revolutionary innovation, but it’s still important that it’s part of the language.
About the author: Ran Bar-Zik is an experienced web developer whose personal blog, Internet Israel, features articles and guides on Node.js, MongoDB, Git, SASS, jQuery, HTML 5, MySQL, and more. Translation of the original article by Aaron Raizen
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment