JavaScript Arrays: Flat and flatMap
Welcome to the Flat and flatMap lesson!
JavaScript arrays' "flat" method flattens nested arrays into a single array. The "flatMap" method combines "map" and "flat" into one method.
This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!
Sometimes, we have data in nested arrays and want to "flatten" these arrays into a single array. For example
[[1,2], [3, 4]]flattens to[1, 2, 3, 4].We can do this ourselves by looping over each element in each subarray.
>
function flatten(arrays) {const flattened = [];arrays.forEach(array => {array.forEach(element => {flattened.push(element);});});return flattened;}flatten([[1, 2],[3, 4]]);Result:
[1, 2, 3, 4]
This solution works, but it's a lot of code to write for such a simple operation. Fortunately, JavaScript already provides this functionality, so we don't have to implement it ourselves. It's an array method called
.flat.>
[[1, 2],[3, 4]].flat();Result:
[1, 2, 3, 4]
By default,
.flatonly flattens one level of arrays, just like ourflattenfunction above. Any arrays nested more deeply than one level are left alone.>
[[1, 2],[3, [4]]].flat();Result:
>
[[1, 2],[3, [4], [[5]]],].flat();Result:
[1, 2, 3, [4], [[5]]]
If we want to flatten beyond the first level, we can provide an optional
depthargument to.flat. The examples above used the default value,1.>
[[1, 2],[3, [4]]].flat(1);Result:
>
[[1, 2],[3, [4]]].flat(2);Result:
[1, 2, 3, 4]
>
[[1, 2],[3, [[4]]]].flat(2);Result:
[1, 2, 3, [4]]
We don't always know how deeply-nested the arrays will be. Moreover, array elements aren't always consistent. Some may be more deeply nested than others. Fortunately, the depth can be
Infinity, in which case.flatwill flatten all arrays no matter how deeply nested they are.>
[[1, 2],[3, [[4]]]].flat(Infinity);Result:
[1, 2, 3, 4]
>
const array = [[[[[[[[[[[[[[1]]]]]]]]]]]]]];array.flat(Infinity);Result:
[1]
.flatonly flattens arrays. Non-array values are left unmodified. For example, we might have an array of objects, where the objects contain more arrays. In that case, both the objects and the arrays inside the objects are left unchanged.>
[[1, 2],{name: 'Amir', postIds: [77, 192]},].flat(Infinity);Result:
Suppose that we're working on a system where each user can have multiple email addresses. We want to build a list of every email address for every user.
There's a nice solution in two steps. First, we build an array of the users' email addresses. Each user has an array of email addresses, so the overall array will be nested one level deep.
>
const users = [{name: 'Amir', emails: ['amir@example.com', 'amir2@example.com']},{name: 'Betty', emails: ['betty@example.com']}];users.map(user => user.emails);Result:
Next, we flatten this array. Since
.mapreturns a new array, we can chain.flatdirectly after.map.Here's a code problem:
Modify the code below to flatten the email array with
.flat. That will give us a single flat array of every user's email address.const users = [{name: 'Amir', emails: ['amir@example.com', 'amir2@example.com']},{name: 'Betty', emails: ['betty@example.com']}];users.map(user => user.emails).flat();- Goal:
['amir@example.com', 'amir2@example.com', 'betty@example.com']
- Yours:
['amir@example.com', 'amir2@example.com', 'betty@example.com']
Combining
.flatwith.mapis common, so there's a built in method to do both:.flatMap. It's identical to calling.mapfollowed by.flat(with the default depth of 1.)>
[{numbers: [1, 2]},{numbers: [3, 4]},].map(obj => obj.numbers);Result:
[[1, 2], [3, 4]]
>
[{numbers: [1, 2]},{numbers: [3, 4]},].map(obj => obj.numbers).flat();Result:
[1, 2, 3, 4]
>
[{numbers: [1, 2]},{numbers: [3, 4]},].flatMap(obj => obj.numbers);Result:
[1, 2, 3, 4]
Here's a code problem:
Use
.flatMapto get a flat array of all of the users' email addresses.const users = [{name: 'Amir', emails: ['amir@example.com', 'amir2@example.com']},{name: 'Betty', emails: ['betty@example.com']}];users.flatMap(user => user.emails);- Goal:
['amir@example.com', 'amir2@example.com', 'betty@example.com']
- Yours:
['amir@example.com', 'amir2@example.com', 'betty@example.com']
Both
.flatand.flatMapbuild and return a new array. The original array remains unmodified.>
const numbers = [1,[2, 3]];numbers.flat();numbers;Result:
[1, [2, 3]]