Step 6: Loops

April 30, 2021

One of the things that computer programs really excel at is repetition. We can easily write a program that would make a computer perform a given operation hundreds or billions of times with a change of a single number. This is one of the things that makes programming so powerful. Once we have an operation that successfully works on one value, it is fairly easy to scale that operation to work on thousands or even millions of values. A loop is a programming structure that allows us to perform these kinds of repeating, iterative operations. There are a bunch of ways to write loops. Newer ways of creating loops have been introduced to JavaScript with the language's development over the years. One of the most foundational ways of creating a loop is by using a while loop.

while Loop

A while loop is a loop that works indefinitely as long as the condition that enables the loop is satisfied. Here is an example of a while loop. Beware that this is an infinite loop. It would run continuously until we somehow terminate the program.

let x = 0;

while (x < 10) {
  console.log("This is an infinite loop.");
}

We start with the keyword while. We then write a condition next to the while keyword inside the parenthesis. This loop will run while this condition is true. We then write the logic that we want to execute inside the loop by using curly brackets. In this example, x is always going to be smaller than 10. This means that the loop will always run. This is an infinite loop.

Infinite loops have their uses, but we usually want to have loops that execute a limited amount of times. We can achieve this using a while loop as well. We just need to ensure that the condition that makes the loop run evaluates to false after a while.

Here is an example of a while loop that runs 10 times.

let x = 0;

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

What is different with this loop is that the value of x does not stay the same. We increase the value of x by 1 every time this loop runs. x = x + 1 means, take the current value of x, add 1 to it and assign the new value to be the current value of x.

Note that we are using the let keyword when declaring the variable x since if we were to use const, we wouldn't be able to assign a new value to x.

Example: Writing a Countdown Function Using a While Loop

Let's create a function that would countdown from 10 to 1. At the end of the countdown, it should display a given text value.

countdown("Launch!");
// Counts down from 10 to 1. Then displays "Launch!" to the screen.
// 10
// 9
// ...
// Launch!

Let's also make it so that the countdown value would be adjustable. If there is no second argument provided, it should countdown from 10. But a second argument can be passed to customize this value to be anything we want (as long as the given value is above 1).

countdown("Launch!", 3);
// Counts down from 3 to 1. Then displays "Launch!" to the screen.
// 3
// 2
// 1
// Launch!

We should start by writing the skeleton for our function.

function countdown(message, startingNumber = 10) {}

countdown("Launch!");

We will create a while loop inside the countdown function and make it start from the startingNumber. The loop should continue while the startingNumber is bigger than 0.

function countdown(message, startingNumber = 10) {
  while (startingNumber > 0) {
    console.log(startingNumber);
    startingNumber = startingNumber - 1;
  }
}

countdown("Launch!");

With each iteration of the loop, we are subtracting 1 from the startingNumber. When the startingNumber reaches 0, the loop will stop working.

The final thing that we need to do here is to make use of the message variable. And let's test it by passing a different startingNumber.

function countdown(message, startingNumber = 10) {
  while (startingNumber > 0) {
    console.log(startingNumber);
    startingNumber = startingNumber - 1;
  }

  console.log(message);
}

countdown("Launch!", 15);

We also need to ensure that the user is passing a starting number that is above 1.

function countdown(message, startingNumber = 10) {
  if (startingNumber <= 1) {
    console.log("Please enter a number bigger than 1.");
    return;
  }

  while (startingNumber > 0) {
    console.log(startingNumber);
    startingNumber = startingNumber - 1;
  }

  console.log(message);
}

countdown("Launch!", 0);

Here we have added a check to see if the startingNumber is above 1. If it is not we are displaying an error message and returning from the function.

for Loop

Since it is very common to write loops that only execute for a given amount of time, JavaScript has a dedicated structure for it. It is called a for loop. Here is how to build a for loop.

for (let counter = 0; counter < 10; counter = counter + 1) {
  console.log("This for a for loop");
}

A for loop has five parts:

  • The for keyword.
  • There is a parenthesis next to the for keyword. Inside there, the first section is for the initialization of a loop variable. We named this variable to be counter in this example, but it can be anything we want.
  • The second section of the parenthesis is the loop condition. The loop will run as long as this condition is true.
  • The third section of the parenthesis is the loop variable incrementation. Here we adjust the loop variable's value so that the loop condition can be satisfied at some point (assuming we are not trying to write an infinite loop).
  • The last part of a for loop is the body statement. This is what gets executed as part of the loop.

Example: Writing a Countdown Function Using a for Loop

Let's rewrite our previous example using a for loop.

function countdown(message, startingNumber = 10) {
  if (startingNumber <= 1) {
    console.log("Please enter a number bigger than 1.");
    return;
  }

  for (
    let countdownNumber = startingNumber;
    countdownNumber > 0;
    countdownNumber = countdownNumber - 1
  ) {
    console.log(countdownNumber);
  }

  console.log(message);
}

countdown("Launch!", 5);

for loop collects every expression needed for managing a loop in one place (inside the parenthesis next to the for keyword). This makes it a bit easier to write and manage a for loop. When writing a while loop, there is a risk of forgetting to increment the loop variable and create an unintentional infinite loop. The structure of the for loop minimizes that risk.

It is worth mentioning that the variable name chosen for for loops are usually much shorter for legibility. Conventionally, the letter i is used as a loop variable name. We can update our example to reflect that convention.

function countdown(message, startingNumber = 10) {
  if (startingNumber <= 1) {
    console.log("Please enter a number bigger than 1.");
    return;
  }

  for (let i = startingNumber; i > 0; i = i - 1) {
    console.log(i);
  }

  console.log(message);
}

countdown("Launch!", 5);

Looping Over Collections

As we have learned earlier arrays and strings are collection-like data types. We can loop over these collections using a while loop or a for loop.

Let's write a function that would display every item of a given array on a separate line.

function displayItems(collection) {
  for (let i = 0; i < collection.length; i = i + 1) {
    console.log(collection[i]);
  }
}

const myFavoriteFood = ["pizza", "sushi", "tacos"];
displayItems(myFavoriteFood);

We wrote a for loop here that starts a loop from number 0 up until the given collection's length, which is equal to 3 in this case. This means that the loop variable will be assigned the values 0, 1, and 2 throughout this loop's execution. We can use these values as indices of items of the given collection.

There are built-in array methods that allow us to iterate over a given array since it is such a common operation to perform.

Two of those methods are forEach and map.

forEach Method

The array's forEach method executes a given callback function for every item in the array.

const myFavoriteFood = ["pizza", "sushi", "tacos"];

myFavoriteFood.forEach(function (item, index, arr) {
  console.log(item);
  console.log(index);
  console.log(arr);
});

The forEach method takes a callback function and provides it with three arguments.

  • The first argument is the current item in the iteration.
  • The second argument is the index of the current item.
  • The third item is the array that we are iterating over.

Using forEach displaying every item in a given array becomes extremely easy.

const myFavoriteFood = ["pizza", "sushi", "tacos"];

myFavoriteFood.forEach(function (item) {
  console.log(item);
});

It is common to use the shorthand function definition syntax (arrow function) when defining inline functions. It reduces the amount of code that we need to write. Let's refactor our example to use an arrow function.

const myFavoriteFood = ["pizza", "sushi", "tacos"];

myFavoriteFood.forEach((item) => {
  console.log(item);
});

Example: Multiplier Function

Let's write a function called multiplier that gets an array of numbers. It should multiply every item in the array with a given value (or with 2 if a value is not provided). Then it should return a new array with the updated values. It should look like this:

const arr = [1, 2, 3, 4, 5];
const newArr = multiplier(arr, 3);

console.log(arr); // [1, 2, 3, 4, 5];
console.log(newArr); // [3, 6, 9, 12, 15];

We are displaying the value of both the given array (arr) and the newArr to ensure that our function does not change, mutate, the original array. As a general rule, functions should not mutate the given values but return a new value whenever possible.

Let's see how to tackle this example both with a for loop and using the forEach method.

This is how we would implement this function using a for loop.

function multiplier(arr, multiplierNum = 2) {
  const result = [];

  for (let i = 0; i < arr.length; i = i + 1) {
    const currentItem = arr[i];
    const newItem = currentItem * multiplierNum;
    result.push(newItem);
  }

  return result;
}

const arr = [1, 2, 3, 4, 5];
const newArr = multiplier(arr, 3);

console.log(arr); // [1, 2, 3, 4, 5];
console.log(newArr); // [3, 6, 9, 12, 15];

We are creating an empty array called result inside the function body to store the results of our multiplication operation. Then we are creating a for loop to iterate over the items of an array. We multiply each item with the given multiplierNum value and then push this new value into the result array. We return the result array after the loop is complete.

Same functionality implemented with forEach method would look like this.

function multiplier(arr, multiplierNum = 2) {
  const result = [];

  arr.forEach((item) => {
    const newItem = item * multiplierNum;
    result.push(newItem);
  });

  return result;
}

const arr = [1, 2, 3, 4, 5];
const newArr = multiplier(arr, 3);

console.log(arr); // [1, 2, 3, 4, 5];
console.log(newArr); // [3, 6, 9, 12, 15];

It is a very common operation to iterate over arrays and alter the items' values to create a new array. Arrays have a method called map that makes these kinds of operations much easier.

map Method

A map method is similar to a forEach method. We pass a callback function to a map method that gets executed on every item.

const myFavoriteFood = ["pizza", "sushi", "tacos"];

myFavoriteFood.map((item) => {
  console.log(item);
});

As we can see, it looks very similar to the forEach method. The arguments that the callback function takes are also the same as the forEach method. The difference is that the map method results in a new array with the items we return from the callback function.

const myFavoriteFood = ["pizza", "sushi", "tacos"];

const myNewFavoriteFood = myFavoriteFood.map((item) => {
  return "ice cream";
});

console.log(myFavoriteFood); // ["pizza", "sushi", "tacos"];
console.log(myNewFavoriteFood); // ["ice cream", "ice cream", "ice cream"];

Using the map method, we are able to construct a new array consisting of whatever we return from the callback function.

Writing a multiplier function is so much easier when using the map method.

function multiplier(arr, multiplierNum = 2) {
  return arr.map((item) => {
    return item * multiplierNum;
  });
}

const arr = [1, 2, 3, 4, 5];
const newArr = multiplier(arr, 3);

console.log(arr); // [1, 2, 3, 4, 5];
console.log(newArr); // [3, 6, 9, 12, 15];

Probably, we wouldn't even write a separate multiplier function since the map method makes it so easy to this operation on the array.

const arr = [1, 2, 3, 4, 5];
const newArr = arr.map((item) => item * 3);

console.log(arr); // [1, 2, 3, 4, 5];
console.log(newArr); // [3, 6, 9, 12, 15];

filter Method

It is a common requirement to go through the items of an array to see if they satisfy a certain condition. For example, imagine having to write a function that would iterate over an array and return all those items that are longer than 4 characters.

function checkLength(arr) {}

const arr = ["pizza", "taco", "egg", "sushi", "ice cream"];
const newArr = checkLength(arr);
console.log(newArr); // ["pizza", "sushi", "ice cream"];

We would need to use a loop or the forEach method to implement this function with our knowledge. map wouldn't be applicable here since map returns something for each item in an array. Here we don't want to return anything if the item is shorter or equal to 4 characters in length.

function checkLength(arr) {
  const result = arr.map((item) => {
    if (item.length > 4) {
      return item;
    }
  });

  return result;
}

const arr = ["pizza", "taco", "egg", "sushi", "ice cream"];
const newArr = checkLength(arr);
console.log(newArr); // ["pizza", undefined, undefined, "sushi", "ice cream"]

Here is what the implementation would look like using a forEach method.

function checkLength(arr) {
  const result = [];

  arr.forEach((item) => {
    if (item.length > 4) {
      result.push(item);
    }
  });

  return result;
}

const arr = ["pizza", "taco", "egg", "sushi", "ice cream"];
const newArr = checkLength(arr);
console.log(newArr); // ["pizza", "sushi", "ice cream"]

This function achieves what we are trying to do, but there is a better way of doing this. It is by using the filter method.

function checkLength(arr) {
  return arr.filter((item) => {
    if (item.length > 4) {
      return true;
    }

    return false;
  });
}

const arr = ["pizza", "taco", "egg", "sushi", "ice cream"];
const newArr = checkLength(arr);
console.log(newArr); // ["pizza", "sushi", "ice cream"]

The filter method takes a callback function that gets provided with the same arguments as forEach or the map method. The filter method iterates over every item and returns a new array that consists of items where the callback function for the item returns true. The item will not be included in the final array if the callback function is to return a false or a falsy value (like undefined).

Summary

In this chapter, we have learned about loops. A loop is a programming structure that allows us to perform repetitive operations easily. There are a bunch of ways to write loops. We have learned about the while and for loops. We can use these structures to loop over arrays or write loops that work indefinitely (infinite loops).

When working with collections like arrays or strings, we frequently need to perform operations on every item of that collection.

  • Given an array of names, we might want to find a person with a specific name.
  • Given an array of numbers, we might want to operate on every number (maybe calculate the square root of all of them.), etc.

This operation of going through every item in a collection is called iteration. We can use while or for loops to iterate over arrays, but some array methods are better suited to this task. These are forEach and map methods.

A forEach method allows us to iterate over each item of an array and execute a callback function on those items. The map method is structurally similar to the forEach method, but it returns a new array consisting of the values returned from the callback function.

We have also learned about the filter method that is also structurally similar to forEach and map methods. The filter is similar to the map method in that it also returns a new array. We can use the filter method to eliminate the items in an array that we don't need.

Find me on Social Media