Implementing JavaScript Flat Method

Implementing JavaScript Flat Method

·

3 min read

In JavaScript, there is a built-in method called flat, which can be used to flat nested array.

const arr1 = [0, 1, 2, [3, 4]];

console.log(arr1.flat());
// expected output: [0, 1, 2, 3, 4]

const arr2 = [0, 1, 2, [[[3, 4]]]];

console.log(arr2.flat(2));
// expected output: [0, 1, 2, [3, 4]]

Let's try to implement it.

Forget about the paramter now, let just make a function to flat an array with any depth.

The most simple solution is to do it recursively.

function flat(arr) {
    let newArr = [];
    for (const ele of arr) {
        if (Array.isArray(ele)) {
            newArr.push(...flat(ele));
        } else {
            newArr.push(ele);
        }
    }
    return newArr;
}

Make use of reduce and concat, we can create a one-line solution.

function flat(arr) {
    return arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flat(val) : val), []);
}

We know that usually there is a limit on recursion depth. So if the array is nested really deep, than this recursive solution may runs into error.

So let's try to implement it iteratively.

function flat(arr) {
    let newArr = [];

    while(arr.length > 0) {
        const ele = arr.shift();
        if (Array.isArray(ele)) {
            arr.splice(0, 0, ...ele);
        } else {
            newArr.push(ele);
        }
    }

    return newArr;
}

As you can see, the idea is pretty the same, but instead of using the recursion stack to store temporary values, we use the original arr to store them.

So now the solution runs ok. Let's try to implement the depth parameter.

function flat(arr, depth) {
    let newArr = [];
    for (const ele of arr) {
        if (depth === 0) {
            newArr.push(ele);
        } else {
            if (Array.isArray(ele)) {
                newArr.push(...flat(ele, depth - 1));
            } else {
                newArr.push(ele);
            }
        }
    }
    return newArr;
}

As you can see, we add a condition to check if depth is zero, if it is, then just use the value, no recursion.

For iterative solution, we need a way to store current depth level. So in below code, we store it along with current value. Every time we walk into deeper level, then make current depth plus 1. So with this, we can stop when current depth value equals to the depth parameter.

function flat(arr, depth) {
    arr = arr.map(ele => ([ele, 0]));

    let newArr = [];

    while (arr.length > 0) {
        const [ele, currentDepth] = arr.shift();
        if (currentDepth === depth) {
            newArr.push(ele);
        } else {
            if (Array.isArray(ele)) {
                arr.splice(0, 0, ...ele.map(e => ([e, currentDepth + 1])));
            } else {
                newArr.push(ele);
            }
        }
    }

    return newArr;
}

OK. That's all. Let's try to test the code in the end.

console.log(flat([1, 2, [3, [4, [5, []]]], 6], 0));
console.log(flat([1, 2, [3, [4, [5, []]]], 6], 1));
console.log(flat([1, 2, [3, [4, [5, []]]], 6], 2));
console.log(flat([1, 2, [3, [4, [5, []]]], 6], 3));
console.log(flat([1, 2, [3, [4, [5, []]]], 6], 4));
console.log(flat([1, 2, [3, [4, [5, []]]], 6], 5));
console.log(flat([1, 2, [3, [4, [5, []]]], 6], Infinity));

/**
[ 1, 2, [ 3, [ 4, [Array] ] ], 6 ]
[ 1, 2, 3, [ 4, [ 5, [] ] ], 6 ]
[ 1, 2, 3, 4, [ 5, [] ], 6 ]
[
  1, 2,  3, 4,
  5, [], 6
]
[ 1, 2, 3, 4, 5, 6 ]
[ 1, 2, 3, 4, 5, 6 ]
[ 1, 2, 3, 4, 5, 6 ]
 */