As of ES2019, there are two new methods on arrays: Array.prototype.flat()
and Array.prototype.flatMap()
.
The Array.prototype.flat()
Method
The flat()
method can flatten a multidimensional array. For example:
[[1, 2], [3, 4, 5], [6]].flat();
// [1, 2, 3, 4, 5, 6]
By default, flat()
will only go one level deep. You can flatten more than 1 level deep by passing a depth argument to flat()
:
[[1, 2], [3, [4, 5]], [6]].flat(2);
// [1, 2, 3, 4, 5, 6]
Here, the depth of this array is 2 since the array at index 1 contains the array [4, 5]
.
The Array.prototype.flatMap()
Method
The flatMap()
method is like calling map()
followed by calling flat(1)
. For example:
['Hello', 'World'].flatMap(word => word.split(''));
// ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd']
If we were to first call map
on this array, this is what we'd get:
['Hello', 'World'].map(word => word.split(''));
// [['H', 'e', 'l', 'l', 'o'], ['W', 'o', 'r', 'l', 'd']]
Then, if we call flat(1)
on this resulting array, we'd get the same result as when we called flatMap()
:
[
['H', 'e', 'l', 'l', 'o'],
['W', 'o', 'r', 'l', 'd'],
].flat(1);
// ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd']
So, flatMap()
is like calling map
followed by flat(1)
.
Writing Your Own flatMap()
Function
Prior to the addition of flatMap()
to JavaScript, I had a flatMap()
utility function in one of my projects. The following was my implementation:
function flatMap(array, callback) {
return array.reduce((accumulator, item) => {
return accumulator.concat(callback(item));
}, []);
}
And you could use it as such:
flatMap(['Hello', 'World'], word => word.split(''));
// ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd']
You could also use flatMap()
in Lodash, which I imagine is more robust and faster.
A Practical Example of flatMap()
One situation I have found flatMap()
to be useful is when dealing with time series data. For example, imagine you get the following JSON from an API:
[
{
"timestamp": "2019-01-15",
"purchases": [
{ "product": "Product 1", "total": 20, "time": "09:00" },
{ "product": "Product 1", "total": 60, "time": "12:00" }
]
},
{
"timestamp": "2019-01-16",
"purchases": [
{ "product": "Product 1", "total": 40, "time": "10:00" },
{ "product": "Product 2", "total": 30, "time": "11:00" }
]
},
{
"timestamp": "2019-01-17",
"purchases": [
{ "product": "Product 1", "total": 10, "time": "14:00" },
{ "product": "Product 2", "total": 20, "time": "17:00" }
]
}
]
If I wanted to plot this data on a chart, I might need to collect the total
for each respective product into a single array, depending on the charting library. I could do that with flatMap()
!
let product1Sales = json.flatMap(({ purchases }) => {
return purchases
.filter(sale => sale.product === 'Product 1')
.map(sale => sale.total);
});
// [20, 60, 40, 10]