Recursive Case

The recursive case is where the function calls itself.

In the above case, until n equals 1 , which would satisfy our base case and end the recursion, we multiply x by the recursive function with the value of n decremented by one. This ensures us we’ll eventually reach our base, and in the meantime, the arithmetic is performed accordingly.

You can think of recursion like eating Oreos. You buy a whole sleeve of Oreos (yum), and you keep eating them until there are none left (well, at least I would. I have no restraint when it comes to cookies). Once there are none left (sad face), stop eating them.

function eatOreos(oreos) {

if (oreos === 0) {

return oreos;

} else {

dunk()

eat()

return eatOreos(oreos-1);

}

} eatOreos(oreos)

In this case, the recursive approach is simpler than an iterative one because all we’re doing is checking the value of oreos , and unless it’s equal to 0 , we invoke the same logic again while decreasing oreos by 1 . For an iterative approach, we’d have to loop through the collection and perform our functionality ( dunk and eat ) at each iteration.

For my case, recursion was more than just a simpler solution. It was fundamentally necessary for my functionality. Here’s a little background.

I have an array, let’s call it company , which contains a list of people who work at the company who are organized by level of authority. Here’s what the people object looks like.

{name: 'Amanda', role: 'President (2019-Present)', authority: [...]}

The authority array contains a list of people that the current person has authority over. Those people also have an authority array, which can contain more people (in some cases none). This process is repeated for each person.

Now imagine we need to render all of the people in lists. Each person is represented as a li item. If that person’s authority value has more people in it, we need to render those people as li elements under a ul element. Repeat this process until we’ve listed every person in the company . If you know exact;y how many levels are present, then you could take an iterative approach.

If you know for certain there are three levels and you need to check all of the people in each person’s authority array, you’d need to write three nested loops to check for their authority value.

const renderCompany = (company) => {

company.forEach(person=>{

//create li, render person

if (person.authority.length > 0){

//create ul, render people in person.authority

person.authority.forEach(x=>{

//create li, render person

if (x.authority.length > 0){

//create ul, render people in person.authority

x.authority.forEach(y=>{

//create li, render person

})

}

})

}

})

} renderCompany(company)

Aside from the fact this is redundant, it’s very inefficient in terms of time complexity. For those of you unfamiliar with time complexity, it’s essentially the amount of time it takes to finish a task relative to the collection. Linear time is represented by O(n), which basically means as the size of your collection increases, the time your function takes to perform will increase in proportion to it.

According to Open4Tech:

“An algorithm is said to have a quadratic time complexity if its running time is proportional to the square of the input.” This is the case when you perform a loop within a loop, you need loop through the collection at every iteration of your original loop.

image from Open4Tech

Since we use three for loops in the above function, the time complexity represented by it is O(n³). This is terribly inefficient. In addition to being inefficient, this will only work under the assumption we know how deeply nested the dataset is. If we don’t know how nested it is, the above function wouldn’t be effective.

While the iterative approach is not sufficient for our task of rendering people in the company , we can still accomplish our goal using recursion. First let’s identify the base case. Remember, the base case is the condition that’s met where we want to stop the function. For renderCompany , we want to stop the function when the person has no people in authority . Conversely, our recursive case is the one we want to execute when the person has a collection of people in their authority array.

Let’s break it down into pseudo code.

1. Loop through company .

2. Create a li element for each person in the company .

3. Check if the person at the current iteration has people in authority .

4. If authority has people in it, create an ul element, append it to the person’s li element, and repeat the above logic for authority in place of company . Otherwise, do nothing.

5. Append all of the li elements created for the company to the DOM .

Now onto the code. Below is our recursive renderCompany function.

const renderCompany = (arr, node) =>{

return arr.map(x=>{

var newListItem = document.createElement("li")

newListItem.innerText = x.role + ': ' + x.name

if (x.authority.length > 0){

var newSubList = document.createElement("ul")

newListItem.appendChild(newSubList)

renderCompany(x.authority, newSubList)

}

if (node){

node.appendChild(newListItem)

}

return newListItem

})

};

Our first argument is an array. When we first call this function, the arr is company , which is our collection of people in the company. We don’t pass an argument for the node . For each item in arr , we use JavaScript's create-element function to create li elements with the line var newListItem = document.createElement("li") . We assign the role of the person and their name to the innerText of newListItem .

Before we return this list item, we check if the person has authority over anyone with the line if (x.authority.length > 0)… . If this is so, we create a sublist and append it to newListItem .

After that, we perform our recursion, this time passing x.authority as our array and the newly created sublist node. Since x.authority is simply a collection of people (as company is), the previous logic can be repeated the same way. Create a li item, assign a value to its innerText , check if it has anybody in authority , and then, finally, check if node is defined. If it is, that means we need to append a newListItem to it.

Once we have a collection of elements, we can append them to our root node in the DOM .

var container = document.getElementById('parentList') var items = renderCompany(company) for (var i = 0; i < items.length; ++i){

container.appendChild(items[i])

}

Below is our rendered list of people in the company and those they have authority over.

By utilizing recursion, our function remains DRY, relatively efficient, and, above all, dynamic. Since it calls the same logic as long as there’s nested data, we don’t need to specify how many loops to perform. It’ll keep performing the recursive steps until we reach our base case: The person has no people in authority .