Lesson Tuesday

In general, we should use Array.prototype.forEach() when we want to loop through every element in an array. If we are using a loop that isn't iterating through an array, we can't use Array.prototype.forEach() for that loop - that's pretty obvious.

However, sometimes we can't use Array.prototype.forEach() when we are looping through an array. Specifically, if we want to stop the loop, we can't use Array.prototype.forEach(). Looping through every element in an array is a requirement of Array.prototype.forEach().

Let's look at an example when this might matter. Let's say we have a series of arrays that hold sequences of DNA. These sequences can potentially be very long - and we want to stop searching an array as soon as we find a pattern. We'll call that pattern "A" (to represent a specific DNA sequence). We'll also add a few other letters to represent other DNA sequences. A biologist won't be impressed by our approximation... but we are programmers and this is a programming example!

Here's our sample "sequence":

const dnaSequence = ["X", "A", "Y", "M", "D"];

In this example, "A" is the second element in this array. We could use an Array.prototype.forEach() loop to flag the sequence if it includes "A". Here's the code:

let dnaFlag = false;
const dnaSequence = ["X", "A", "Y", "M", "D"];
dnaSequence.forEach(function(element) {
  if (element === "A") {
    dnaFlag = true;
  };
  console.log("Looped!")
});
dnaFlag

In the code above, when an iteration through the loop finds a match with "A", it will switch the dnaFlag boolean to true. Using boolean flags in this manner is very common in coding.

Note, however, that the second character in our array matched the letter "A". What if our array were a million elements long? (DNA sequences can be very complex.) Our loop needs to loop through all million elements - even though we really only need to do two iterations of the loop to switch dnaFlag to true. This would be horribly inefficient. We've added a console.log("Looped!") to show how many times this loop runs.

In order to break out of loop, we need to use the break; keyword. This keyword does exactly what it sounds like: stops a loop.

What happens if we try to add a break; statement to the loop above?

let dnaFlag = false;
const dnaSequence = ["X", "A", "Y", "M", "D"];
dnaSequence.forEach(function(element) {
  if (element === "A") {
    dnaFlag = true;
    break;
  };
});
dnaFlag

We'll get the following error: Uncaught SyntaxError: Illegal break statement. In short, we can't break out of this kind of loop.

However, we can use a for loop to break out of this loop. Try the following out in the console:

let dnaFlag = false;
const dnaSequence = ["X", "A", "Y", "M", "D"];
for (let i = 0; i < dnaSequence.length; i +=1) {
  if (dnaSequence[i] === "A") {
    dnaFlag = true;
    break;
  };
  console.log("Looped!");
}
dnaFlag

We've added a console.log("Looped!") to this code, too - and you'll see it only triggers once. The second time through the loop, the condition is met and the loop breaks due to the break; keyword.

There's something I don't like about the code above. As of now, the dnaFlag variable is globally scoped. In an ideal world, we'd wrap this all in a function. Let's do this now, not just because it's better code but also so we can see what happens when we try to return out of both a for loop and an Array.prototype.forEach() loop.

We'll start by wrapping the for loop in a function called dnaPatternDetector:

function dnaPatternDetector(dnaSequence, pattern) {
  for (let i = 0; i < dnaSequence.length; i +=1) {
    if (dnaSequence[i] === pattern) {
      return true;
    };
    console.log("Looped!");
  }
  return false;
}

We've done a bunch of things to refactor this code. Our function takes two parameters. The dnaSequence is an array of characters. The pattern is the character we are looking for (we are looking for "A").

We actually don't need to use the dnaFlag anymore. If the pattern is met (dnaSequence[i] === pattern), then the loop will stop running and our function will return true. If the pattern is never met, our loop will complete and the final part of our function will be reached: return false;.

Let's try the following out in the console. First, some variables - a sequence variable that includes an array and two variables for patterns: pattern1 and pattern2:

const sequence = ["X", "A", "Y", "M", "D"];
const pattern1 = "A";
const pattern2 = "Z";

Next, our function. We will need it:

function dnaPatternDetector(dnaSequence, pattern) {
  for (let i = 0; i < dnaSequence.length; i +=1) {
    if (dnaSequence[i] === pattern) {
      return true;
    };
    console.log("Looped!");
  }
  return false;
}

Finally, try this out:

dnaPatternDetector(sequence, pattern1);

We'll see that the function returns true - exactly what we'd expect because sequence includes "A". We also see that it only hits the console.log("Looped!") statement once. return has the same effect as the break; statement here - however, it doesn't just break us out of the loop, it returns from the function.

Now try out the second pattern, which is a character that's not included in sequence:

dnaPatternDetector(sequence, pattern2);

Our loop iterates through every character in the array and never finds the character "Z". The loop completes (so "Looped!" is printed to the screen five times). This is the only way the final part of the code can be reached: return false. The function returns false.

A function that actually checks DNA sequences would probably be quite a bit more complicated than this example. However, this does show a potential use case for when we'd want to break out of a loop - either with a break; or a return statement. This same function wouldn't work as intended with an Array.prototype.forEach() loop - if we try to put a return statement inside the loop, we'll get an error. The rules for Array.prototype.forEach() loops are strict - we must iterate through every element in the array.

To summarize, you should favor Array.prototype.forEach() loops over for loops except in the following cases:

  • You're not looping through an array. Array.prototype.forEach() can only be used with arrays.
  • You need to break out of the loop - either with break; or a return statement.

We can add one more case just for the sake of practice:

  • You should favor for loops even for looping through every element in an array until you understand exactly how they work. They are a cornerstone of JavaScript and it's essential to understand them even if you'll mostly use other types of loops in the future.

Note that later in this section we'll learn about another kind of loop that will make our lives a bit easier - and that we can use to break out of a loop. For now, though, keep practicing with for loops!

Lesson 17 of 31
Last updated June 22, 2020