Learn by Doing Lodash _.flatten & company

_.flatten(array)

Flattens array a single level deep.

_.flattenDeep(array)

Recursively flattens array.

_.flattenDepth(array, [depth=1])

Recursively flatten array up to depth times.

See the full documentation for these methods here.


Over and over and over and over…

The first time I froze my browser trying to run a script with an incorrectly structured recursive function, it felt like I had broken the world. Since then I’ve tended to shy away from them: it just feels wrong to call a function within itself. But_.flattenDeep forces me to utilise recursion, and _.flattenDepth makes it especially important that I understand exactly how and why it works.

Flatten

_.flatten is pretty straightforward: just push each value in the array to the new result array. O(n) — in fact, all the following methods should also be O(n) depending upon your definition of the input.

Since we’re technically flattening a single layer deep, we only have to evaluate the inner arrays once; we don’t care what they contain as long as we can copy it.

FlattenDeep

_.flattenDeep is almost the same as _.flatten, but we start playing with recursion. We do care what’s inside the inner arrays, since we want our final result to be totally ‘flat’. If we hit an array within an array, we pass the flatten() function again… which will run again if we hit another array within the array we’re evaluating… which will run again if we hit another array within that array…

Ultimately, it just grabs everything and pushes it into our result array. Exactly as we want.

FlattenDepth

_.flattenDepth cares about how many arrays we’ve seen during the evaluation, but when we’re dealing with a recursive function how do we keep a counter? Honestly, I struggled here: I kept worrying about the internal scope of each new flatten. You think the solution at this point would be obvious, but it took a couple of tries for me to realise that I needed to move the counter outside the function altogether.

The log statements in the CodePen below might help if you don’t understand why this works.


Overall, I feel a little more comfortable using recursion after trying to replicate this method. Admittedly it took longer than I’d like, but next time I run into an problem requiring a recursive function I’ll be a little faster.

But! Did you notice something ugly in the _.flattenDepth code?

depth = depth === undefined ? 1 : Math.floor(depth);
if (l === 0 || Number.isNaN(depth)) {
return arr;
}

I’ve been running into weird results from Lodash while running my test cases. I make it a point to pass in unexpected arguments in my tests, and passing an array, string, or null for depth causes _.flattenDepth to return the original array. An undefined depth defaults to 1, but if you pass a decimal it will run it through a function which I’m roughly equating to Math.floor.

While this is most likely done to keep Lodash fairly user-friendly, I’m still debating on the value of these weird edge cases. You can see all my test cases in the GitHub repo here.