DEV Community

loading...
Cover image for Understanding Array.prototype.flatMap

Understanding Array.prototype.flatMap

Laurie
Software dev at Netflix | DC techie | Conference speaker | egghead Instructor | TC39 Educators Committee | Girls Who Code Facilitator | Board game geek | @laurieontech on twitter
Originally published at tenmilesquare.com ・2 min read

Last week we talked about the new flat method available in ES2019.

This week, we're going to dive into flatMap!

Let's start with map

In JavaScript arrays a have a built-in function called map. It takes a function and uses that function to operate on each item in the array. Something like this.

let arr = [1, 2, 3]

let result = arr.map((item) => item * 2);
Enter fullscreen mode Exit fullscreen mode

result is then an array with each item from arr multiplied by two.

[2, 4, 6]
Enter fullscreen mode Exit fullscreen mode

Another way to work with map

You can also use map to create a data structure inside each array element! Let's look at this example.

let arr = [1, 2, 3]

let result = arr.map((item) => [item, item * 2]);
Enter fullscreen mode Exit fullscreen mode

In this case, our result is this.

[[1, 2],[2, 4],[3, 6]]
Enter fullscreen mode Exit fullscreen mode

It's common to want to flatten this.

So you have

let arr = [1, 2, 3]

let result = arr.map((item) => [item, item * 2]).flat();

[1, 2, 2, 4, 3, 6]
Enter fullscreen mode Exit fullscreen mode

But not with flatMap!

With the addition of flatMap this becomes simpler!

let arr = [1, 2, 3]

let result = arr.flatMap((item) => [item, item * 2]);

[1, 2, 2, 4, 3, 6]
Enter fullscreen mode Exit fullscreen mode

It's the same thing.

Important

The default argument for flat is 1, NOT Infinity. This is important because flatMap is the same way. Let's look at an example.

let arr = [1, 2, 3]

let result = arr.flatMap((item) => [item, [item]]);

[ 1, [1], 2, [2], 3, [3] ]
Enter fullscreen mode Exit fullscreen mode

The array is not fully flattened because flatMap only flattens one level.

Conclusion

flatMap is a great built-in function for this popular pattern! Check it out and let me know what you think.

Discussion (19)

Collapse
terabytetiger profile image
Tyler V. (he/him)

Is there a way to pass Infinity into flatmap()? Or would you need to do a Array.flatMap(itme => item).flat(Infinity) roundabout to get that effect? (I'm not even sure if this would be particularly useful in any scenario)

Collapse
citizen428 profile image
Michael Kohl

In the context of FP in languages like Haskell, flatmap is basically a monadic bind (equivalent to join + fmap), which essentially ends up removing one “layer of wrapping”, i.e. one layer of nested arrays. The list/array implementation is a special case of a much more generic construct, so the idea of arbitrary flattening does not generally apply.

Collapse
selbekk profile image
selbekk

You had to go all monad? 😄

Thread Thread
citizen428 profile image
Michael Kohl • Edited

I mean I could also just have waived my hands and pretended there isn't a reason it behaves the way it does, but I figured some people at least may actually be interested in understanding the why.

Thread Thread
selbekk profile image
selbekk

Haha totally fair 😄 i was just trolling. Also, I need to learn what monads are.

Thread Thread
citizen428 profile image
Michael Kohl • Edited

Also, I need to learn what monads are.

Less than most people believe. Essentially "containers" for composing computations. Need something that can be absent? Option/Maybe to the rescue. Computation that can fail? Either/Result is your friend. Collection-like behavior? Here's a list monad. Add to that a function that takes a non-monadic value and "wraps it" and another one to "get out" the currently contained value again and you're pretty much done.

Example: you can't easily compose computations where intermediate values can be null, without sprinkling if statements everywhere. On the other hand you can compose these computations in the Option monad, as fmap will pattern match on the constructors and either perform the operation on the current value "inside" the monad, or pass through a None if the computation has failed at an intermediate step. This potentially replaces a whole lot of if statements with a single pattern match at the very end.

Hope that helped. For more detail maybe try the IMHO excellent Don't Fear The Monad talk by Brian Beckman.

Collapse
laurieontech profile image
Laurie Author

Not that I've seen. I think the way to do it would be the second example you listed.

Collapse
tanmayrajani profile image
Tanmay Rajani

If I have more than one operations to do on an array, I subconsciously go to reduce everytime. It's better than remembering these separate functions I think. Not sure if it's the best option complexity-wise..

Collapse
laurieontech profile image
Laurie Author

I think it depends. The Mozilla docs actually mention that for large arrays that option isn’t as efficient.

developer.mozilla.org/en-US/docs/W...

Collapse
tanmayrajani profile image
Tanmay Rajani

Makes sense. Thanks for sharing!

Collapse
lesha profile image
lesha 🟨⬛️

I really hate the fact that libs like lodash are so much more useful than standard library and so happy that some of these features are making their way to standard library.

Hope they add mapValues/mapKeys/pickBy next. Having map/filter equivalents for objects is really convenient

Collapse
dukeofetiquette profile image
Adam DuQuette

Code school instructor here, I'd love to see a real-world example in your posts. The simple example is great for the intro, but having a more robust example to solidify might help. I am constantly getting that type of request from students, so I've gotten in the habit of trying to incorporate them during lecture.

That is to say, why are flat() and flatMap() being introduced? Why should a junior developer be excited about these?

Thanks : )

Collapse
laurieontech profile image
Laurie Author

Fair point. Been keeping these intros bite-sized, but I'll certainly consider adding a bit more!

Collapse
georgecoldham profile image
George

I have never really understood a use for this. I feel like it is useful, but I just cant find how!

Collapse
laurieontech profile image
Laurie Author

It's a functional programming pattern for sure.

Collapse
georgecoldham profile image
George

I love functional programming, its what I push for all my code to be where possible (I often work with other peoples code bases).

I just cant think of a situation where I have got an array that I both need to iterate over and flatten at the same time.

Thread Thread
laurieontech profile image
Laurie Author

Seems like this has some good ones! 2ality.com/2017/04/flatmap.html

Collapse
chuckwood profile image
Charles Wood

Isn't flatMap in ES6 (aka ES2015)?

Collapse
laurieontech profile image
Laurie Author

Nope! New in ES2019. Map itself is ES2015.