How to Flatten an Array in JavaScript
As I was interviewing for PayPal, my recruiters kept stressing that I needed to know how to “flatten” an array. I recall this advice and my first thought was… “what does that even mean”? As the recruiter then explained, flattening an array is the process of taking nested array elements and basically putting them all into one “flat” array. All I have to do is turn some small arrays into one big array. Once I learned that I figured how hard could it be, right?
Actually, it was kind of difficult. At least if you haven’t done it before. Thankfully I solved the problem successfully, and with time to spare, I might add. But it actually made me wonder something; is there an easier way to do this? Is there a better way to flatten an array?
Well, thankfully, there is, and that’s what this list is about. Here are a few examples:
Using while
loop with a recursive helper function
This solution runs a while loop that pushes each element into a new array. When the next element of an array is a nested array, the function recursively calls itself and does the same for its contents, until all nested arrays have been pushed into the new array.
const flatten = (nested) => {
const flat = [];
const handleFlat = (array) => {
let counter = 0
while (counter < array.length) {
const val = array[counter];
if (Array.isArray(val)) {
handleFlat(val);
} else {
flat.push(val)
}
counter++;
}
}
handleFlat(nested);
return flat;
}console.log(flatten(a)); // [1, 2, 3, 4, 5, 6, 7];
Using Reduce
and Concat
Method
This solution acts similarly to the first, but only flattens arrays one level deep. You can use concat
to add both single elements and elements of a nested array into our accumulator array. The same can be achieved with just concat
using the spread syntax, passing each decomposed element into a new array while being passed in as concat arguments.
const arr = [1, 2, [3, 4]];// To flat single level array
arr.flat();
// is equivalent to
arr.reduce((acc, val) => acc.concat(val), []);
// [1, 2, 3, 4]// or with decomposition syntax
const flattened = arr => [].concat(...arr);
Using reduce
, concat
, and isArray
, recursively
This function is similar to the method above with a twist. It checks each value in our reduce function to see if its value is an array. If the value is an array, then we recursively call our flatDeep method as many levels deep as we define in our depth, or d
parameter.
const arr = [1, 2, [3, 4, [5, 6]]];// to enable deep level flatten use recursion with reduce and concat
function flatDeep(arr, d = 1) {
return d > 0 ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatDeep(val, d - 1) : val), [])
: arr.slice();
};flatDeep(arr, Infinity);
// [1, 2, 3, 4, 5, 6]
Use a stack
This solution is a stack, which essentially means that we just loop through our array in a last in first out order, taking from the back of the array. We make a copy of our array, and start popping elements off the back and pushing them into our new array. Before we push each element into our result array, we check if that element is an array. If it is, then we take the contents of that array and push them to the end of our array. Those elements will be added in the same order as before, just no longer in a nested array. If one of those elements happens to have an array, the process will repeat itself.
Because we use push
and pop
to add elements in our result array, the array will be backwards, so we use the reverse
method to flip our array back in the correct order. This is slightly more efficient than using shift
and unshift
, as those methods would force JavaScript to re-index the array after each iteration.
// non recursive flatten deep using a stack
// note that depth control is hard/inefficient as we will need to tag EACH value with its own depth
// also possible w/o reversing on shift/unshift, but array OPs on the end tends to be faster
function flatten(input) {
const stack = [...input];
const res = [];
while(stack.length) {
// pop value from stack
const next = stack.pop();
if(Array.isArray(next)) {
// push back array items, won't modify the original input
stack.push(...next);
} else {
res.push(next);
}
}
// reverse to restore input order
return res.reverse();
}const arr = [1, 2, [3, 4, [5, 6]]];
flatten(arr);
// [1, 2, 3, 4, 5, 6]
Use a generator function
The topic of what generators
are is for another day. For now, just know that generators are functions that act as iterables until a condition is met. In this case, we create an iterator function that loops over each index of an array. When we reach an element that is another array, we recursively call our function. Because it is a generator, instead of waiting for our recursive function to finish and return all of our values at once, our recursive function will instead yield each element of the nested array as it is encountered.
Since generator functions return iterators, we use the spread syntax to iterate over our old array and return a new one.
function* flatten(array, depth) {
if(depth === undefined) {
depth = 1;
}
for(const item of array) {
if(Array.isArray(item) && depth > 0) {
yield* flatten(item, depth - 1);
} else {
yield item;
}
}
}const arr = [1, 2, [3, 4, [5, 6]]];
const flattened = [...flatten(arr, Infinity)];
// [1, 2, 3, 4, 5, 6]
Using Array.flat()
This function is the reason I even decided to make an article on this. JavaScript thankfully knew that this was a thing and decided to make a function out of it. By default, it only flattens an array one level deep, but you can pass in a number to define as many levels deep as you want to go. If you don’t care how deep you drill, just pass in the Infinity
keyword.
const arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]const arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]const arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]const arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]/* Flatten Array Holes */
const arr5 = [1, 2, , 4, 5];
arr5.flat();
// [1, 2, 4, 5]
I hope this list helps you, and happy coding.
Source: Code comes from MDN Array.prototype.flat()
Originally published at https://devingray.io on December 10, 2020.