For an explanation of Recursion, click here.

Click here to share this article on LinkedIn »

Why have I just spent an hour clicking a link that just loops back to this page?!

Two reasons:

That’s recursion. I’m hoping it will give me better stats.

If you don’t understand why this is recursion, and you really spent an hour clicking the same link, then the aim of this article is for you to understand this joke.

Why am I writing this?

When I was learning development; recursion was always one of those things that I sort of understood, thought was really awesome, but just couldn’t ever find a use case for.

I was recently working on a problem where I needed to flatten multi-dimensional Objects into a flat structure. It was a problem that was easily resolved using recursion; and one that I thought would work well in a blog post.

So what are we building?

This part is simple; we’ll write a function that, given a multi-dimensional Object, will return a one-dimensional object with all of the key-value pairs of the nested objects.

Notice that we’re only interested in the keys that actually have a value associated to them; not those that are assigned to nested Objects.

The function

Let’s see the whole function and then we can talk through each step together.

Let’s now walk through this line by line.

Create an empty object that we’ll add to and return.

let obj = {};

2. Get the keys of the object that has been passed into the function. If you had an object {one: 1, two: 2, three: { four: 4 }} then the keys would be one , two and three ( four is not as it’s nested ). We then use the forEach function to loop over each key .

Object.keys(object).forEach(key => {});

3. Then we check the type of the value ; if it isn’t an object then we add it to our obj Object and we move on.

if (typeof object[key] !== 'object') {

obj[key] = object[key];

}

4. The next step is where we use recursion. If the type of the value is an Object (e.g. three: { four: 4 } three is the key and its value is an Object), then we recursively call the flattenObject function to return a one dimensional Object that we can then merge with obj

else {

obj = { ...obj, ...flattenObject(object[key]) };

}

Makes sense!

No, of course it doesn’t! Even I’m a bit confused. Let’s go through point 4 in more detail.

Instead of our big object, with multiple levels of nested objects, let’s start with a simpler one;

const obj = {one: 1, two: { three: 3}, four: 4}

Here’s what happens when we pass this to our flattenObject function

obj is assigned to an empty object

is assigned to an empty object An array is returned from Object.keys(object) ( [one, two, four] )

( ) We then loop over that array using forEach

The first key ( one ) has a value of 1 , which is not an Object, so it’s added to obj .

) has a value of , which is not an Object, so it’s added to . obj now equals {one: 1}

now equals The second key ( two ) has a value of an Object, so we’re going to recursively call the flattenObject function.

) has a value of an Object, so we’re going to recursively call the function. The flattenObject function is called with the object[key] which, in this example, is object[two] and therefore {three: 3 }

function is called with the which, in this example, is and therefore Once more a variable ( obj ) is created that equals an empty Object ( {} ). It is important to note that this variable is completely different to the one from the first invocation of this function; each call has it’s own place on the Call Stack and therefore it’s own scope.

) is created that equals an empty Object ( ). It is important to note that this variable is completely different to the one from the first invocation of this function; each call has it’s own place on the Call Stack and therefore it’s own scope. An array is returned from Object.keys(object) ( [three] )

( ) We then loop over that array using forEach (you should now start seeing a similarity to the bullet points above)

(you should now start seeing a similarity to the bullet points above) The first key ( three ) has a value of 3 , which is not an Object, so it’s added to obj .

) has a value of , which is not an Object, so it’s added to . There are no further items to loop over in the array, so obj is returned.

is returned. Remembering that we called the function recursively, the returned Object is then passed into the spread operator

// Changing this

obj = { ...obj, ...flattenObject(object[key]) }; // To this

obj = { ...{one: 1}, ...{three: 3} };

And because the spread operator concatenates the two objects together, obj now equals {one: 1, three: 3}

The third key ( four ), remembering the object we passed was {one: 1, two: { three: 3}, four: 4} , has a value of 4 which is not an Object, so it’s added to obj .

), remembering the object we passed was , has a value of which is not an Object, so it’s added to . obj now equals {one: 1, three: 3, four: 4} .

now equals . There are no further items to loop over in the array, so obj is returned.

A quick warning

Although using recursion can look really smart, and feel great, it should be used with caution. Remember each recursive call will add another call to the Call Stack which could blow up in your face (Maximum call stack size exceeded). You should consider reading about tail-recursion, or maybe I’ll write another blog post about it (stay tuned!).

That’s it

That is how you can use recursion to change a multi dimensional Object into a one dimensional Object. Here’s the code once more: