Loops and Iterating

Most programs require code that runs over and over until some ending condition occurs. Most programming languages, including JavaScript, use loops to provide this capability. JavaScript loops have several forms, but the main looping structures use a looping keyword, a condition, and a block. These loops execute the loop's body (the block) for as long as the condition remains truthy. We use the term one iteration to describe executing the loop body once. JavaScript also has two other loop mechanisms: array abstractions and recursion. We'll see all of these mechanisms in this chapter.

Let's start with the while loop.

while Loops

A while loop uses the while keyword followed by a conditional expression in parentheses and a block. The loop executes the block again and again for as long as the conditional expression remains truthy. In most programs, that loop should ultimately stop repeating. That means that the block must do something that tells JavaScript when the loop should stop; that is, it needs to arrange for the conditional expression to become falsy. Otherwise, the loop is an infinite loop that never stops repeating.

To see why a while loop is useful, consider writing some code that logs numbers from 1 to 10:

console.log(1);
console.log(2);
console.log(3);
console.log(4);
console.log(5);
console.log(6);
console.log(7);
console.log(8);
console.log(9);
console.log(10);

While that code is straightforward and readily understood, it's easy to see that this approach isn't sustainable. Suppose we need to log numbers from 1 to 1000 or 1,000,000. If you had to write all that code for such a simple task, you would soon come to regret your career choice.

Let's rewrite the program with a while loop. Create a file named counter.js with the following code and run it:

let counter = 1;
while (counter <= 10) {
  console.log(counter);
  counter = counter + 1;
}

This code does the same thing as the first program, but more programmatically. If you want to log 1000 numbers or even a million, all you have to change is the conditional expression:

let counter = 1;
#highlight
while (counter <= 1000) {
#endhighlight
  console.log(counter);
  counter = counter + 1;
}

When JavaScript encounters this while loop, it evaluates the conditional expression inside the parentheses, counter <= 1000. Since counter's initial value is 1, the expression is true at the beginning of the while statement and the engine runs the loop's block. Inside the block, we output counter's value, then increment it by 1.

After the first block iteration, JavaScript re-evaluates the conditional expression. This time, counter is 2, which is still less than or equal to 1000; thus, the block runs again. After 1000 iterations, counter's value becomes 1001. Since the loop condition is no longer truthy, the program stops looping and continues with the first expression or statement after the loop.

Line 4 in this example is crucial. The loop block must modify counter in some way that ultimately makes the loop condition false. If it doesn't, the loop becomes an infinite loop, which, in most cases, you don't want. If your program never stops running, you probably have an infinite loop somewhere in the program. Try running the example code after commenting out line 4. You'll see that the number 1 keeps printing to the console. If you're using node, you can use the Control+c keystroke to terminate the program. If you're using the browser console, you may have a tougher time; in Chrome, you can use the task manager from the Window menu to force-close the tab.

Here's a neat trick that's useful when incrementing values:

let counter = 1;
while (counter <= 10) {
  console.log(counter);
  #highlight
  counter += 1;
  #endhighlight
}

In this code, we changed the line counter = counter + 1 to counter += 1. This syntax appears often in other programming languages. It's a succinct way to say the same thing as counter = counter + 1, but with less typing. You can use this syntax with other arithmetic operators as well:

> let a = 3
> a *= 4            // 3 * 4
= 12

> a /= 6            // 12 / 6
= 2

> a -= 2            // 2 - 2
= 0

JavaScript has an even more succinct syntax to increment a number by 1.

let counter = 1;
while (counter <= 10) {
  console.log(counter);
  #highlight
  counter++;
  #endhighlight
}

The increment operator (++) increments its operand by 1; that is, it adds 1 to the existing value. There's a corresponding decrement operator (--) that decrements a variable's value by 1. That is, it subtracts 1 from the value. JavaScript provides these operators since incrementing and decrementing by 1 are such commonplace operations.

There are two forms of ++: one that comes before the variable name (the pre-increment operator), and one that comes after (the post-increment operator). Both increment the variable, but they differ in what gets returned by the expression. The pre-increment form returns the new value of the variable, while the post-increment form returns the previous value of the variable.

> let a = 1;
> ++a;
= 2

> a
= 2

> a++
= 2

> a
= 3

There are corresponding pre-decrement and post-decrement operators (e.g., --a and a--) that work in a similar way.

There's a growing sentiment among some developers that the increment and decrement operators are harmful. It's easy to mistype them in ways that can lead to strange bugs, especially if you're not mindful of the return values. They recommend using the += and -= operators instead; it's only a few characters more to type.

Most developers still use them in the increment clause of a for loop:

for (var index = 0; index < 5; ++index) {
  // body of loop
}

However, they shouldn't be used anywhere else.

Looping Over Arrays With while

One of the most common uses of loops in programming is to iterate over an array's elements and perform some action on each element. By iterate, we mean that we process each element one at a time, in sequence from the first to the last element.

Arrays are fundamental structures in programming; nearly every language has them. They are useful in most programs, and the ability to read and manipulate the elements of an array is a critical skill. Let's write a program that iterates over the names in an array of names and creates a new array with the names in uppercase:

let names = ['Chris', 'Kevin', 'Naveed', 'Pete', 'Victor'];
let upperNames = [];
let index = 0;

while (index < names.length) {
  let upperCaseName = names[index].toUpperCase();
  upperNames.push(upperCaseName);
  index += 1;
}

console.log(upperNames); // => ['CHRIS', 'KEVIN', 'NAVEED', 'PETE', 'VICTOR']

A bit of explanation is in order here. The variable names holds an array of names. We want to convert every name to uppercase and append it to the empty upperNames array. Since array indexes are zero-based, we initialize an index variable with 0.

Next, we use a loop that executes as long as the number in index is smaller than the length of the names array. Line 8 increments the index by 1 after each iteration, which ensures that index < names.length becomes false after the loop handles the last element.

Line 6 accesses the name stored at names[index] and uses it to call the toUpperCase method. That method returns the name in uppercase, which we assign to upperCaseName. It doesn't change the original name in the names array.

Line 7 uses the push method for arrays to append the latest uppercase name to the upperNames array. Over the five iterations of the names array, line 7 appends five uppercase names to upperNames, one per iteration, in the same order that the loop processes them.

Note that we initialized names, upperNames, and index before the loop. If we initialized them inside the loop, they would have block scope. Every loop iteration would create, initialize, and discard each variable. That wouldn't work well even if the code could run, which it would not.

do/while Loop

A do/while loop differs visibly from a while loop, but its behavior is almost identical. The crucial difference is that do/while always executes the code in the block at least once. A while loop can't make that guarantee since the initial condition may be falsy; if it is, the loop body doesn't run. In a do/while loop, the conditional check occurs at the end of the loop instead of the beginning which allows it to run the code at least once, even if the condition is falsy when the loop begins.

Let's write some code that illustrates how do/while works. We'll ask the user whether he wants to repeat an action, and then repeat the question if he enters y.

let answer;
do {
  answer = prompt("Do you want to do that again?");
} while (answer === 'y');

When you run this code in a browser, it displays a Do you want to do that again? prompt. If you enter the lowercase letter y, it displays the prompt again. It repeats this process until you enter something other than y, at which point the loop ends and control moves to the first statement or expression after the loop.

Notice how while and the condition are now at the end of the loop. Since the test occurs at the end of the loop, the loop always executes at least once.

Take some time to play with while and do/while loops.

for Loops

for loops have the same purpose as while loops, but they use a condensed syntax that works well when iterating over arrays and other sequences. A for loop combines variable initialization, a loop condition, and the variable increment/decrement expression all on the same line:

for (initialization; condition; increment) {
  // loop body
}

This structure behaves almost the same as:

initialization;
while (condition) {
  // loop body
  increment;
}

The sole difference between the two loops is the scope of any variables declared by the initialization clause. In the while statement, the scope includes the code that surrounds the loop; in the for statement, the scope is the for statement and its body.

Let's rewrite the names program with a for loop to better illustrate the difference:

let names = ['Chris', 'Kevin', 'Naveed', 'Pete', 'Victor'];
let upperNames = [];

for (let index = 0; index < names.length; index += 1) {
  let upperCaseName = names[index].toUpperCase();
  upperNames.push(upperCaseName);
}

console.log(upperNames); // => ['CHRIS', 'KEVIN', 'NAVEED', 'PETE', 'VICTOR']

This program functions in the same way as the version that uses while. The difference is that we initialize the index variable, specify the loop condition, and increment the index variable all on the same line. When JavaScript runs this loop, it follows this sequence:

  1. Declare and initialize the index variable to 0.
  2. If index is not less than the array length, go to step 6.
  3. Execute the loop body.
  4. Increment index.
  5. Go back to step 2.
  6. Log the results.

for loops let you see and understand the looping logic at a single glance. The syntax also lets you move the index variable from the global scope into the scope of the for statement, and it helps make your code cleaner and more organized.

Controlling Loops

JavaScript uses the keywords continue and break to provide more control over loops. continue lets you start a new iteration of the loop, while break lets you terminate a loop early.

continue

Let's continue working with the names program. Suppose we want all the uppercase names in our upperNames array except 'Naveed'. The continue statement can help us do that.

let names = ['Chris', 'Kevin', 'Naveed', 'Pete', 'Victor'];
let upperNames = [];

for (let index = 0; index < names.length; index += 1) {
  if (names[index] === 'Naveed') {
    continue;
  }

  let upperCaseName = names[index].toUpperCase();
  upperNames.push(upperCaseName);
}

console.log(upperNames); // => ['CHRIS', 'KEVIN', 'PETE', 'VICTOR']

The upperNames array no longer contains 'NAVEED'. When a loop encounters the continue keyword, it skips running the rest of the block and jumps ahead to the next iteration. In this example, we tell the loop to ignore 'Naveed' and skip to the next iteration without adding 'NAVEED' to upperNames.

You can rewrite a loop that uses continue with a negated if conditional.

let names = ['Chris', 'Kevin', 'Naveed', 'Pete', 'Victor'];
let upperNames = [];

for (let index = 0; index < names.length; index += 1) {
  if (names[index] !== 'Naveed') {
    let upperCaseName = names[index].toUpperCase();
    upperNames.push(upperCaseName);
  }
}

console.log(upperNames); // ['CHRIS', 'KEVIN', 'PETE', 'VICTOR']

This code behaves like the version that uses continue.

If we can write looping logic without continue, why bother using it at all? You don't have to use continue, of course, but it often leads to a more elegant solution to a problem. Without continue, your loops get cluttered with nested conditional logic.

for (let i = 0; i < someNumber; i += 1) {
  if (someCondition) {
    // execute 10 lines
  }
}
for (let i = 0; i < someNumber; i += 1) {
  if (someCondition) {
    // some code here
    if (anotherCondition) {
      // some more code here
    }
  }
}

We can use continue to rewrite those two loops without nesting:

for (let i = 0; i < someNumber; i += 1) {
  if (!someCondition) continue;
  // execute 10 lines
}
for (let i = 0; i < someNumber; i += 1) {
  if (!someCondition) continue;
  // some code here

  if (!anotherCondition) continue;
  // some more code here
}

Earlier, we said that you should always use blocks with if statements. A common exception to this rule occurs when using a continue, break, or return statement as the if clause. When changing the flow with these three statements, the single-line version of the if statement can make your code easier to read.

break

You sometimes want to skip all remaining iterations of a loop. For instance, when you search an array for a specific value, you probably want to stop searching once you find it. There's no reason to keep searching if you don't need any subsequent matches.

Let's explore this idea with some code. Create a file named search.js with the following code and run it in your browser or Node:

let array = [3, 1, 5, 9, 2, 6, 4, 7];
let indexOfFive = -1;

for (let i = 0; i < array.length; i += 1) {
  if (array[i] === 5) {
    indexOfFive = i;
  }
}

console.log(indexOfFive);

This program iterates over the elements of an array to find the element whose value is 5. When it finds that value, it stores its index in the indexOfFive variable. It then logs the index of the found element to the console. Notice that we initialize indexOfFive to -1: we use this value when the array doesn't have the desired value.

If you study this code, you should notice that the loop continues executing once it finds the element whose value is 5. It iterates over every element in the array, even if 5 is the first element. That seems pointless and wasteful of precious CPU resources. That's where break steps in and saves the day:

let array = [3, 1, 5, 9, 2, 6, 4, 7]
let indexOfFive = -1;

for (let i = 0; i < array.length; i += 1) {
  if (array[i] === 5) {
    indexOfFive = i;
    #highlight
    break;
    #endhighlight
  }
}

console.log(indexOfFive);

The break statement on line 7 tells JavaScript to terminate the loop once we find the desired element.

Array Iteration

JavaScript arrays have several methods that iterate over the elements without using the looping syntax we've seen thus far.

As you may recall, arrays are ordered lists. Suppose you have an array of names that you want to display on the console, one per line. How would you do that without using one of the loop structures we've seen so far?

One way is to use the built-in forEach method for arrays:

let names = ['Chris', 'Kevin', 'Naveed', 'Pete', 'Victor'];

names.forEach(function(name) {
  console.log(name);
});

Isn't that nice! It's probably the most peculiar code you've seen so far. We've got some explaining to do.

The most glaring item in need of explanation is that we seem to be passing a function definition as an argument to forEach. Think about that. How in the world could that be valid, or even useful?

If you study this code long enough, you may recognize that the function definition is, in fact, a function expression: we talked about them back in the Functions chapter. This function expression doesn't have a name: it's an anonymous function.

That doesn't help, however. We still don't understand how the code works.

One feature of JavaScript that sets it apart from most other languages is that it has first-class functions. That means that functions are values: you can assign them to variables, pass them around as arguments to other functions, and even use them as return values in other functions. In our example, we're passing the anonymous function as an argument to forEach. That explains why the code is valid.

When you pass a function as an argument to another function, that other function can call the function represented by the argument. That's what forEach does, and it's why this code is useful. As its name suggests, forEach loops through each element in an array, in sequence, starting with the first element. For each name, forEach invokes the anonymous function with the name as an argument. The anonymous function can do whatever it needs to do with the argument. In this case, it merely logs the name.

We can make the code more concise by using an arrow function:

let names = ['Chris', 'Kevin', 'Naveed', 'Pete', 'Victor'];

names.forEach(name => console.log(name));

Isn't that cool? There are several other iterating methods in JavaScript; over time, you'll probably use them all. For now, know that most JavaScript programmers prefer to use array looping abstractions like forEach to loop over arrays.

We'll learn about abstractions that perform transformation and filtering of arrays in the Arrays chapter. We'll also explore first-class functions in much more detail in our courses at Launch School.

Recursion

Before starting this section on Recursion, you may want to review the material on the call stack from the Functions chapter. Understanding the call stack will help you better understand recursion.

Recursive functions are functions that call themselves. Such code doesn't look much like a loop, but there's a close relationship between loops and recursion. The relationship is close enough that we say that recursion is another way to create loops in JavaScript.

That probably sounds confusing so let's look at some code to get a better idea.

A Simple Example

Suppose you want to know the result of doubling a number, then the result of doubling that number, and so on until the number reaches some predefined maximum, such as 50. You might begin with the following function:

function doubler(number) {
  console.log(number * 2);
}

You can use it like this, stopping when you get a result greater than 50.

> doubler(1)     // => 2
> doubler(2)     // => 4
> doubler(4)     // => 8
> doubler(8)     // => 16
> doubler(16)    // => 32
> doubler(32)    // => 64

That's a bit clumsy, and the code doesn't lend itself to reuse.

Let's refactor doubler to use recursion instead:

function doubler(number) {
  console.log(number);

  if (number <= 50) {
    doubler(number * 2);
  }
}

doubler(5); // => 5
            // => 10
            // => 20
            // => 40
            // => 80

Our doubler function code calls itself on line 5. Each invocation of doubler logs a new number, then calls itself with a new value twice that of the input number. To make sure the "loop" stops, we skip the recursive call when the number is greater than 50.

A Complex Example

You may need to read this example several times before you grasp how it works. That's normal. Recursion is tricky and difficult to fathom, so take your time. Don't panic. Try to understand every line of code. It's easy to get frustrated, so take a deep breath and try to relax.

It's also a "math question," which causes some students distress, often unnecessarily. Here, we use addition, subtraction, and a tiny bit of algebra.

If you do have problems with this section, put it aside for now. We don't expect you to write recursive code this complex right away. Instead, try to understand how it works. If you can walk away from this example and feel like you understand it, you should be able to understand most recursive code that you'll encounter in the future. However, if you have trouble, that doesn't mean you have a problem. Come back to it when you feel more comfortable using JavaScript; it should make more sense later.

Let's create a function that uses recursion to calculate the nth number in the Fibonacci sequence. Each number in this sequence is the sum of the previous two numbers in the sequence:

  • fibonacci(0) = 0
  • fibonacci(1) = 1
  • fibonacci(2) = fibonacci(1) + fibonacci(0) = 1 + 0 = 1
  • fibonacci(3) = fibonacci(2) + fibonacci(1) = 1 + 1 = 2
  • fibonacci(4) = fibonacci(3) + fibonacci(2) = 2 + 1 = 3
  • fibonacci(5) = fibonacci(4) + fibonacci(3) = 3 + 2 = 5
  • fibonacci(6) = fibonacci(5) + fibonacci(4) = 5 + 3 = 8
  • fibonacci(7) = fibonacci(6) + fibonacci(5) = 8 + 5 = 13

In general, we can write:

fibonacci(0) = 0 // by definition
fibonacci(1) = 1 // by definition
fibonacci(n) = fibonacci(n - 1) + fibonacci(n - 2) // for all n >= 2

That's the key expression needed to solve this problem.

Create the following file:

function fibonacci(number) {
  if (number < 2) return number; // 0 if number is 0, 1 if number is 1
  return fibonacci(number - 1) + fibonacci(number - 2);
}

console.log(fibonacci(6));  // => 8
console.log(fibonacci(20)); // => 6765

A diagram can help, as can walking through an example or two. For instance, We can use a tree-like structure to understand the mechanics of the Fibonacci program. The following diagram uses f to abbreviate fibonacci to save space:

image

Each time the fibonacci function runs, it recursively invokes itself twice, once with a number 1 less than the current number, and once with a number 2 less than the current number. For instance, let's calculate f(6) by hand. Find the f(6) box in the diagram and follow along:

  1. f(6) returns the value of f(5) + f(4).
  2. f(5) returns the value of f(4) + f(3).
  3. f(4) returns the value of f(3) + f(2).
  4. f(3) returns the value of f(2) + f(1).
  5. f(2) returns the value of f(1) + f(0).
  6. f(1) returns 1. The baseline condition number < 2 tells us this value.
  7. f(0) returns 0. The baseline condition number < 2 tells us this value.
  8. Plug the values from steps 6 and 7 into step 5 to determine that f(2) is 1.
  9. Plug the values from steps 5 and 6 into step 4 to determine that f(3) is 2.
  10. Plug the values from steps 4 and 5 into step 3 to determine that f(4) is 3.
  11. Plug the values from steps 3 and 4 into step 2 to determine that f(5) is 5.
  12. Plug the values from steps 2 and 3 into step 1 to determine that f(6) is 8.

The recursive calls work their way down the tree: f(6) calls f(5) and f(4); f(5) calls f(4) and f(3). In the end, we reach the lowest level in the tree which always has a 1 or 0 return value in this algorithm. Once it reaches the bottom, the code starts adding return values and percolating back up the tree. When it works its way back to the top, it returns the final result.

The fact that this code calls itself twice makes it more complicated than most recursive code. For instance, we must calculate f(4) twice: once on the left, once on the right. However, the principle behind recursion remains the same.

Every recursive function has a baseline condition that marks the end of the recursion (number < 2 in our code) and some code that recursively calls the function with a new argument. In most cases, the baseline condition returns a concrete value that gets reused as the code "unwinds" the recursive calls. Each unwind step uses the previous return value(s) to calculate an intermediate result that gets returned in the next step. In our example, the last step evaluates f(6) as f(5) + f(4) which yields 8.

We don't expect you to write any recursive programs this complicated. However, if you understand how it works, you should be able to code some simpler programs. Thinking about recursion requires time and practice, but once you get the hang of it, it can simplify a wide range of problems.

Summary

Loops and array looping abstractions are a great way to perform repeated operations on a data set. Often, in JavaScript, you'll find yourself reaching for an array abstraction before a loop, but not all the time. Recursion, the ability to call a method inside of itself, can also solve some complex problems. Let's test these concepts with some exercises!

Exercises

  1. Modify the age.js program you wrote in the exercises for the Input/Output chapter. The updated code should use a for loop to display the future ages.

    Solution

    let readlineSync = require('readline-sync');
    let age = readlineSync.question('How old are you? ');
    age = parseInt(age);
    console.log(`You are ${age} years old.`);
    for (let future = 10; future <= 40; future += 10) {
      console.log(`In ${future} years, you will be ${age + future} years old.`);
    }
    
  2. Write a function that computes and returns the factorial of a number by using a for loop. The factorial of a positive integer n, signified by n!, is defined as the product of all integers between 1 and n, inclusive:

    n! Expansion Result
    1! 1 1
    2! 1 * 2 2
    3! 1 * 2 * 3 6
    4! 1 * 2 * 3 * 4 24
    5! 1 * 2 * 3 * 4 * 5 120

    You may assume that the argument is always a positive integer.

    Examples

    console.log(factorial(1));     // => 1
    console.log(factorial(2));     // => 2
    console.log(factorial(3));     // => 6
    console.log(factorial(4));     // => 24
    console.log(factorial(5));     // => 120
    console.log(factorial(6));     // => 720
    console.log(factorial(7));     // => 5040
    console.log(factorial(8));     // => 40320
    

    Solution

    function factorial(number) {
      let result = 1;
      for (let counter = number; counter > 0; counter -= 1) {
        result *= counter;
      }
    
      return result;
    }
    
    console.log(factorial(1));     // => 1
    console.log(factorial(2));     // => 2
    console.log(factorial(3));     // => 6
    console.log(factorial(4));     // => 24
    console.log(factorial(5));     // => 120
    console.log(factorial(6));     // => 720
    console.log(factorial(7));     // => 5040
    console.log(factorial(8));     // => 40320
    
  3. The following code causes an infinite loop (a loop that never stops iterating). Why?

    let counter = 0;
    
    while (counter = 1) {
      console.log(counter);
      counter += 1;
    
      if (counter > 2) {
        break;
      }
    }
    

    Solution

    The problem occurs on line 3 where we assign 1 to counter inside the conditional part of the while loop. JavaScript accepts this code since the assignment always returns a truthy value (1 in this case), the loop condition never becomes false. Furthermore, the test on line 7 never becomes true since the assignment on line 3 ensures that counter is always equal to 2 when we execute line 7.

  4. Does the following code produce an error? Why or why not? What output does this code send to the console?

    for (let i = 0; i < 5;) {
      console.log(i += 1);
    }
    

    Solution

    The code doesn't produce an error since all 3 components of the for loop are optional. In this code, we omit the "next value" component; however, this isn't a problem here since we increment the loop variable on line 2.

    The code outputs the following:

    1
    2
    3
    4
    5
    

    Although i is 0 on the first iteration, the loop logs 1 during that iteration since i += 1 increments i before console.log gets to log anything. i += 1 also returns the new value of i (1), and that's what gets passed to console.log. Similar actions occur on each iteration: the output is always 1 greater than the initial value of i, and i += 1 takes care of incrementing i to the next higher number, then passes that value to console.log.

    Note that we use let in the initialization clause of the loop to create and initialize an index variable. We can also write:

    let i;
    for (i = 0; i < 5;) {
      console.log(i += 1);
    }
    

    While these examples may seem identical at first glance - they both log the numbers 1 through 5 to the console - there is a subtle difference. In the first example, JavaScript limits the scope of the resulting variable to the for loop. Code outside of the loop can't access the value after the loop ends. In the latter example, using let before the loop creates a variable whose scope extends beyond the loop. In particular, code that appears after the loop can examine and use the value of the variable.

    To avoid problems with name clashes, you should limit the scope of your variables to the smallest possible scope. As a result, some developers prefer to declare their index variable in the for initialization clause. However, the practice isn't universal; you'll understand why that is when you learn about the var statement.

  5. The following code uses a randomNumberBetween function to generate a number between its first and second arguments. It uses a while loop to try to generate a number greater than 2. Refactor the code so that you don't need to call randomNumberBetween from two different locations, lines 6 and 10. Do not change the arguments you pass to randomNumberBetween.

    function randomNumberBetween(min, max) {
      return Math.floor(Math.random() * (max - min + 1) + min);
    }
    
    let tries = 0;
    let result = randomNumberBetween(1, 6);
    tries += 1;
    
    while (result <= 2) {
      result = randomNumberBetween(1, 6);
      tries += 1;
    }
    
    console.log('It took ' + String(tries) + ' tries to get a number greater than 2');
    

    Solution

    function randomNumberBetween(min, max) {
      return Math.floor(Math.random() * (max - min + 1) + min);
    }
    
    let tries = 0;
    let result;
    
    do {
      result = randomNumberBetween(1, 6);
      tries += 1;
    } while (result <= 2);
    
    console.log('It took ' + String(tries) + ' tries to get a number greater 2');
    

    The ideal use case for do...while occurs when you need to execute some code at least once like we do here.

  6. Reimplement the factorial function from exercise 2 using recursion. Once again, you may assume that the argument is always a positive integer.

    Examples

    console.log(factorial(1));     // => 1
    console.log(factorial(2));     // => 2
    console.log(factorial(3));     // => 6
    console.log(factorial(4));     // => 24
    console.log(factorial(5));     // => 120
    console.log(factorial(6));     // => 720
    console.log(factorial(7));     // => 5040
    console.log(factorial(8));     // => 40320
    

    Solution

    function factorial(number) {
      if (number === 1) {
        return 1;
      }
    
      return number * factorial(number - 1);
    }
    
    console.log(factorial(1));     // => 1
    console.log(factorial(2));     // => 2
    console.log(factorial(3));     // => 6
    console.log(factorial(4));     // => 24
    console.log(factorial(5));     // => 120
    console.log(factorial(6));     // => 720
    console.log(factorial(7));     // => 5040
    console.log(factorial(8));     // => 40320