What is a Recursion in Javascript?

What is a Recursion in Javascript?

It is normal for a function to call other functions in Javascript, including itself. When a function calls itself, it is known as a recursive function. Could you compare the examples below? In Case 1, the funcA() function was called in funcB(). That is a regular function.

Case 1

function funcA() {
}
function funcB() {
funcA()
}

But in Case 2, funcD() calls itself within its declaration. Such functions are known as recursive functions or recursion. Of course, the recursion below still misses some other key ingredients that make it fulfil the basic requirements of a proper recursion. And that is why I am writing this article. Everything will be explained in proper detail.

Case 2

function funcD() {
    funcD()
}

What is Recursion

Let’s say your boss asks you to sort a huge folder containing over a hundred files, with each file name written boldly on its covers. You approach it by sorting the files according to the first letters on the file names. Then, probe into each letter-sorted pile to sort them maybe based on the date-of-entry. Here, the solution to sorting the pile of files is within itself. And each sorting round leads to another sorting round until there is no more to do.

In a recursion, a function is used to solve a problem in which the solution leads to solving another problem within the same function. And the loop goes on and on until no problem is left to be solved. This helps the function solve a task by breaking it into smaller and manageable sub-tasks.

Instead of writing long and disorganised lines of code, a recursion function saves you time, and effort with just a few lines of programming.

But by always recalling itself this means the function can run infinitely. Hence, why a recursive function needs a base case, that activates when the requirements of the recursion ends (when all problems have been solved).

In the code example below, the recursiveFunc() returns itself to solve a problem as far as the base case is false. Once the base case returns true, the recursiveFunc() stops.

// the function declaration
const recursiveFunc = () => {
  if (true) {
  // the base case 
    return
}
  // the recursive call
  recursiveFunc()
};

As can be deduced from the example above, a recursive function requires three basic conditions to fulfil its recursion requirements. They include

  1. Function declaration: Whether you use a regular function or a callback, you declare a recursive function the same way you define any other function. I.e function myFunction() {} or const myFunction = () => {}

  2. Recursive call: This is the most important condition for a recursion. It is the part where the function calls itself within its body, which makes the function keep repeating itself. In the earlier stated example, the recursive call is the recursiveFunc() called within the function body.

  3. Base case: This is what terminates the function and eliminates further recursion once all required problems have been solved within. It prevents the function from running infinitely. Often, a base case is defined within an if condition with the recursion outside, or vice versa.

Use Cases of Recursion in Real-Life and Javascript Scenarios

Wait, when and how was recursion first discovered?

Recursion seems like a concept applicable to every real-life scenario (even if we had been using it before realizing it). However, a mathematician named Edouard Lucas first realized the term by happenstance in 1883 while solving Fibbionaci numbers. And since then, it has been a mainstay in solving programming problems.

Fibonacci Numbers with Recursion

Fibonacci numbers are a classic example of recursion. The function solves the problem for smaller Fibonacci indices until it reaches the base case.

function fibonacciRecursive(n) {
    if (n <= 1) return n; // Base case
    return fibonacciRecursive(n - 1) + fibonacciRecursive(n - 2); // Recursive step
}

console.log(fibonacciRecursive(6)); // Output: 8

Explanation: In this example, the function calculates Fibonacci numbers by breaking the problem into smaller Fibonacci calculations. The recursion stops when n≤1n \leq 1, as those are predefined values (0 or 1).

Factorial with Recursion

Factorials are another simple use case for recursion. The factorial of nn is the product of nn and the factorial of n−1n - 1, with a base case of n=0n = 0 or n=1n = 1.

function factorialRecursive(n) {
    if (n === 0 || n === 1) return 1; // Base case
    return n * factorialRecursive(n - 1); // Recursive step
}

console.log(factorialRecursive(5)); // Output: 120

Explanation: Here, the recursive function multiplies nn by the factorial of n−1n - 1, reducing the problem until nn is either 0 or 1, at which point recursion stops.

Searching in a Tree with Recursion

Recursion is essential when working with hierarchical data like trees. For example, searching for a value in a tree structure often involves recursive traversal.

const tree = {
    value: 1,
    children: [
        { value: 2, children: [] },
        { value: 3, children: [{ value: 4, children: [] }] },
    ],
};

function searchTree(node, target) {
    if (node.value === target) return true; // Base case
    for (const child of node.children) {
        if (searchTree(child, target)) return true; // Recursive step
    }
    return false; // If not found
}

console.log(searchTree(tree, 4)); // Output: true
console.log(searchTree(tree, 5)); // Output: false

Explanation: This recursive function searches through each node of the tree. If the current node’s value matches the target, it returns true. Otherwise, it recursively searches the children. If no match is found, it returns false.

Sorting with Recursion (Merge Sort)

Sorting algorithms like merge sort use recursion to divide the problem into smaller subproblems and solve them individually.

function mergeSort(arr) {
    if (arr.length <= 1) return arr; // Base case

    const mid = Math.floor(arr.length / 2);
    const left = mergeSort(arr.slice(0, mid)); // Recursive step
    const right = mergeSort(arr.slice(mid));  // Recursive step

    return merge(left, right);
}

function merge(left, right) {
    const result = [];
    while (left.length && right.length) {
        if (left[0] < right[0]) {
            result.push(left.shift());
        } else {
            result.push(right.shift());
        }
    }
    return [...result, ...left, ...right];
}

console.log(mergeSort([5, 3, 8, 4, 2])); // Output: [2, 3, 4, 5, 8]

Explanation: Merge sort recursively divides the array into halves until each subarray contains one element (base case). It then merges the sorted subarrays back together in sorted order.

Traversing Hierarchies with Recursion

Recursion simplifies navigating hierarchical structures, like organization charts or DOM trees. Each level of the hierarchy is treated as a smaller instance of the same problem.

const hierarchy = {
    name: "CEO",
    subordinates: [
        {
            name: "Manager 1",
            subordinates: [
                { name: "Employee 1", subordinates: [] },
                { name: "Employee 2", subordinates: [] },
            ],
        },
        {
            name: "Manager 2",
            subordinates: [{ name: "Employee 3", subordinates: [] }],
        },
    ],
};

function printHierarchy(node) {
    console.log(node.name); // Base case and action
    for (const subordinate of node.subordinates) {
        printHierarchy(subordinate); // Recursive step
    }
}

printHierarchy(hierarchy);
// Output:
// CEO
// Manager 1
// Employee 1
// Employee 2
// Manager 2
// Employee 3

Explanation: The recursive function processes the current node (printing its name) and recursively calls itself for each subordinate. The base case is when a node has no subordinates.

Key Takeaways

Recursion is a powerful programming concept where a function solves a problem by repeatedly calling itself, breaking the problem into smaller, manageable subproblems. This approach simplifies complex tasks like sorting, navigating hierarchical data, or solving mathematical problems like Fibonacci numbers and factorials. However, to prevent infinite loops, every recursive function must include a base case that terminates the recursion once the problem is fully resolved. By leveraging recursion effectively, developers can write concise and elegant solutions for otherwise intricate tasks, showcasing its utility in both real-life and JavaScript scenarios.