308H.3 - Loops and Iteration

Learning Objectives

By the end of this lesson, learners should be able to:

  • Implement JavaScript loops.
  • Iterate through strings and other iterables.

Loops

A "loop" in programming is a sequence of instructions that are repeated either a certain number of times or until a condition is met. Loops are very common structures that are used to accomplish repetitive tasks, iterate and process collections of data, handle mathematics, and much more.

There are many different kinds of loops used for many different purposes, all of which we will cover in this lesson:

  • for
  • for...in
  • for...of
  • while
  • do...while
  • labeled statements

The for and while loops are the most common loop types in the vast majority of applications.

We will also go into detail on how the break and continue control flow statements affect loops.


For Loops

For loops are typically used to perform a task a set number of times. A for loop repeats until the specified condition evaluates to false, as follows:

for (initialization; condition; afterthought) {
	statements
}

When a for loop executes, the following occurs:

  1. The initializing expression initialization, if any, is executed. This expression usually initializes one or more loop counters, but the syntax allows an expression of any degree of complexity. This expression can also declare variables.
  2. The condition expression is evaluated. If the value of condition is true, the loop statements execute. Otherwise, the for loop terminates.
  3. The statements execute.
  4. If present, the update expression afterthought is executed.
  5. Control returns to Step 2.

Let's look at a very simple example:

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

Try this, and see what the results are. Note the value at which the loop ends.

  • Initialization, let i = 0, is executed before the loop starts.
  • Condition, i < 10, defines the condition for running the loop.
  • Afterthought, i++, is executed after each time the loop has been executed.

The for loop tests the condition given before the execution of each loop. As long as it evaluates to true, the loop will run again. When the condition evaluates to false, the loop exits and the program continues. At the end of each loop, the for loop executes the afterthought, which should push the loop further towards its conditional statement returning false.

If a loop never reaches a false conditional, the loop will never stop. We call these "infinite loops," and they are bad. Inifinite loops will block the rest of your program from executing and freeze the platform they're operating on, often causing an error to be thrown.

Exercise

Write some for loops to accomplish the following tasks:

  • Count down from 10 to 1.
  • Output odd numbers from 1 to 10.
  • Output even number from 1 to 10.
  • Output multiples of 3, starting at 6 and ending at 60.
  • Output an increasing number of # symbols, from 1 to 7, as shown below.
#
##
###
####
#####
######
#######

Now, write a for loop that iterates from 1 to 20. The loop should:

  • Print “prime” for all prime numbers.
  • Print “even” for all even numbers.
  • Print “odd” for all odd numbers.
  • Treat 2 as an even number and 1 & 3 as odd, rather than prime.

Looping through Iterables

Before we continue with other types of loops, let us use the for loop structure to explore one of the ways loops are often used.

Many data types in JavaScript are considered iterable. The technical definition of what makes something iterable is beyond the scope of this lesson, but the short version is that iterables are built in such a way that they can be used easily with loops and other iterative structures.

One of the only iterable data types we have covered thus far is strings. Let's examine how we could loop through a string to print every character individually using a for loop:

const str = "Hello World";

for (let i = 0; i < str.length; i++) {
	console.log(str[i]);
}

In this example, we're using the length property of strings to set our conditional, so that no matter what length of string we pass into the for loop, it will always execute the correct number of loops. We're also using square-bracket notation to access every individual character of str by using i as the index.

We will go into more detail on these kinds of iterations and their syntax when we cover other complex data types like arrays and objects, but it is important to understand that these tools exist as you approach problems and develop solutions.

Other loop structures are made specifically for dealing with iterables, as we will soon see.


For ... In Loops

The for...in statement iterates through a set of enumerable properties of an object using a specified variable. For each distinct property, JavaScript executes the statements defined within the loop.

Since we have not yet covered objects or properties, let's look again at the familiar string example. What happens if we do this:

const str = "Hello World";

for (const i in str) {
	console.log(str[i]);
}

In the case of strings, the for...in statement gives us the indexes of each character, which we've set to i. Note that we could have named this variable anything we wanted.

With other objects and data collections, this loop behaves slightly differently. As always with things you are uncertain about, reference the documentation when you come across other iterables that you might want to use with the for...in structure.


For ... Of Loops

While a for...in statement accesses the properties of an iterable like a string, the for...of loop accesses the values associated with those properties. In many ways, this lets us skip a step when we do not need to know the name or index of a value for any reason.

Looking at our string example again, this is how you would accomplish the same goal with a for...of loop:

const str = "Hello World";

for (const c of str) {
	console.log(c);
}

In this example, we can assign each character directly to the variable c.


The Continue Statement in For Loops

The continue control flow statement behaves slightly differently depending on the loop in which it is used, but the general outcome is the same. It will stop execution of all remaining code within the loop, and move to the next iteration of the loop.

In a for loop, the continue statement jumps to the afterthought expression. For example, if we wanted to skip every letter "l":

const str = "Hello World";

for (let i = 0; i < str.length; i++) {
	if (str[i] == "l") {
		continue;
	}

	console.log(str[i]);
}

In a for...in or for...of loop, the continue statement simply jumps to the next iteration (since there is no afterthought expression):

const str = "Hello World";

for (const i in str) {
	if (str[i] == "l") {
		continue;
	}

	console.log(str[i]);
}

for (const c of str) {
	if (c == "l") {
		continue;
	}

	console.log(c);
}

While Loop Structure

In a while loop, as long as the condition is true, the code within is executed. The program will re-test the condition with each iteration of the loop, and as long as it still evaluates to true, the loop will run again. When the condition evaluates to false, the loop exits and the program continues with the next statement.

This is very similar to how a for loop works, except in a while loop there is no initialization statement and no afterthought. Any logic that progresses the loop toward completion must occur within the loop itself.

When a break statement is encountered inside of a loop, the loop is immediately exited and the program continues with the statement immediately following the loop.

Here's an example of a while loop that takes a number x and divides it by 2 before subtracting 1. If the value of x is ever not a whole number, the loop breaks:

let x = 30;

while (x > 0) {
	x /= 2;
	x--;

	if (x % 1 != 0) {
		break;
	}
	
	console.log(x);
}

How could this loop be accomplished with a for loop instead of a while loop?

let x = 30;

for (x = x/2 - 1; x >= 0; x = x/2 - 1) {
	if (x % 1 != 0) {
		break;
	}

	console.log(x);
}

What are the benefits of using for or while in certain circumstances? In these simple examples, the answer may not be apparent. In practice, however, the correct loop structure will often choose itself. Syntax complexity, code clarity, readability, and performance are all considerations when choosing the appropriate loop structure.

The following is an example of a while loop that iterates a series of numbers as long as it is less than 10. It is adding 2 to the variable a after every iteration. This example is written in Python syntax, not JavaScript, but the premise of loops in programming is universal.

The following is an example of a while loop that iterates through a series of numbers as long as a is less than 7. It is also running an if...else statement to check if the variable a is divisible by 2 with no remainder, and printing it with a string “is even” or “is odd.”

Exercise

Write while loops to accomplish the following tasks:

  • Count down to 0 from a given number.
  • Log integers in multiples of 3 as long as they are less than 35.
  • Print integers in multiples of 5 as long as they are less than 100.
  • Print integers between 0 and 20 with the following conditions:

    • All numbers divisible by 2 should be multiplied by 3 before they are output.
    • All other integers should not be output.
  • Print all prime numbers between 0 and 20.

Bonus Logical Question

Romeo went to the vending machine to buy himself a cookie, which costs $4. He paid with a $10 bill, and the vending machine gave him his change in quarters.

  • Write a loop that outputs how many quarters Romeo received.

Do While Loop

Another variation on the while loop is the do...while loop.

The do keyword indicates that a block of code should be executed, and then the while statement that follows loops that block of code evaluates as you would expect it to. The key difference with a do...while loop is that the block of code will always execute at least once, regardless of the result of the evaluated condition. This is because the block of code comes before the while section.

let x = 10;

do {
	x--;
	console.log(x);
} while (x > 50);

The above example will still log 9 to the console, despite the loop never activating.


The Continue Statement in While Loops

The continue statement in a while loop skips the remainder of the current loop iteration, just like in for loops. The major difference with while loops is that there are no built-in statements that progress toward the completion of the loop, nor are you iterating over an iterable like a string which has a finite ending.

This means that an improper continue statement within a while loop can easily cause an infinite loop.


Labeled Statements

A label provides a statement with an identifier that allows you to reference it elsewhere within a program. Labels are applied by adding any identifier that is not a reserved word and following it with a colon.

You can identify any statement with a label, but we're going to focus on the context of labeled loops and how they work alongside the break and continue statements, as this is the most common use case by far.

Here's an example of a labeled while loop:

myLoop: while (condition) {
	// do the things
}

Labeling loops is particularly valuable when dealing with nested loops that have control flow statements such as break and continue. By default, these statements only affect the innermost loop that they are located within.

For example:

let x = 1;
let y = 1;

myLoop: while (true) {
	console.log(`Outer loop ${x}.`);
	x++;

	while (true) {
		console.log(`Inner loop ${y}.`);
		y++;

		if (x == 5 && y % 5 == 0) {
			break myLoop;
		} else if (y % 5 == 0) {
			break;
		}
	}
}

Doing this allows us to break the outer loop from within the inner loop, which would otherwise not be possible.

Similarly, we can continue labeled loops from within nested loops.

let x = 1;
let y = 1;

myLoop: while (true) {
	console.log(`Outer loop ${x}.`);
	x++;

	while (true) {
		console.log(`Inner loop ${y}.`);
		y++;

		if (x == 5 && y % 5 == 0) {
			break myLoop;
		} else if (y % 5 == 0) {
			continue myLoop;
		}
	}
}

This example using continue achieves the same result as the previous example with break, but is perhaps a bit more descriptive.

Copyright © Per Scholas 2024