Start learning free

LESSON 113 OF 348 · FOR LOOPS - LOOPING OVER ARRAYS

Infinite For Loops in TypeScript: Changing an Array While Looping

Lesson Read the full explanation

We also use for loops to iterate over sequences like strings and arrays. We can run into problems when we manipulate a sequence while iterating on it.

For example, if we append elements to an array while iterating on it:

let fruits = ["banana", "orange"];
for(let fruit of fruits) {
  fruits.push("kivi");
}

Every time we enter this loop, we add a kivi item to the end of the array that we are iterating through.

As a result, we never make it to the end of the array. It keeps growing forever!

This is an infinite for loop. You can imagine that as programmers, we want to make sure we never write infinite loops as they make our program run forever and completely unusable.


Assignment
Follow the Coding Tutorial and let's practice with infinite for loops!


Hint
Look at the examples above if you get stuck.


STEP 1 OF 1
    Ctrl + Enter to check

    TYPESCRIPT · FOR LOOPS - LOOPING OVER ARRAYS

    Infinite For Loops 2 in TypeScript, explained

    Loops that never end

    A loop is infinite when nothing ever makes it stop. The program keeps repeating the body, never reaches the lines after the loop, and keeps using memory or processor time until someone stops it. Usually that is a bug.

    A for...of loop over an array looks safe, since it has no condition to get wrong: it ends when it runs out of elements. That safety holds only while the array has a fixed size. If the body keeps adding elements to the array the loop is walking, the end keeps moving away.

    How adding to the array you walk over goes wrong

    let fruits: string[] = ["banana", "orange"];
    for (let fruit of fruits) {
        fruits.push("kiwi");
    }
    

    A for...of loop over an array checks, before each pass, whether there is another element after the one it just handled. It checks against the array as it is at that moment, not against a size recorded when the loop started. Here is the flow:

    1. Pass 1 handles "banana" and pushes a kiwi. The array now has three elements.
    2. Pass 2 handles "orange" and pushes another kiwi. Four elements.
    3. Pass 3 handles the first kiwi, which is the third element, and pushes yet another. Five elements.
    4. Every pass moves one position to the right, and every pass adds one element on the right. The loop is always one step behind the end and never reaches it.

    Nothing is reported while this happens, because the code is valid. The array grows until the program is stopped or runs out of memory, which is why the lesson calls it a loop that never makes it to the end.

    The exercise

    The task is to add every name from newStudents to students and then print all six names. The code you start with reads:

    let students: string[] = ["Andy", "Mary", "Alex"];
    let newStudents: string[] = ["Ana", "Alexa", "Mike"];
    
    for (const student of newStudents) {
        newStudents.push(student);
    }
    
    for (const student of students) {
        console.log(student);
    }
    

    The loop walks over newStudents, and its body pushes onto newStudents too. It is the pattern from the example above: each pass adds an element to the array that is being walked, so the first loop never ends, and the printing loop is never reached.

    The intent is to read from one array and write to another. Reading and writing the same array is what causes trouble.

    1. Keep for (const student of newStudents) as the loop header. It reads each new name.
    2. In the body, push onto students, not newStudents. The array being walked now stays at three elements, so the loop ends after three passes.
    3. Leave the second loop alone. It prints every element of students, which now has six.
    let students: string[] = ["Andy", "Mary", "Alex"];
    let newStudents: string[] = ["Ana", "Alexa", "Mike"];
    
    for (const student of newStudents) {
        students.push(student);
    }
    
    for (const student of students) {
        console.log(student);
    }
    
    Andy
    Mary
    Alex
    Ana
    Alexa
    Mike
    

    Safe ways to change an array during a loop

    • Write to a different array, as above. This is the cleanest fix when you are collecting results.
    • Loop over a copy. Spreading makes a shallow copy that has a fixed size, so the original can grow safely: for (const fruit of [...fruits]) { fruits.push(fruit); } doubles ["banana", "orange"] into ["banana", "orange", "banana", "orange"] and stops.
    • Record the length first. With a counting loop, const len = vals.length; before the loop and i < len in its condition fix the number of passes, whatever happens to the array inside.

    Removing elements while looping is a quieter version of the same problem. If a body calls shift() on the array being walked, the elements slide left under the loop and one is skipped: looping over [1, 2, 3, 4] and removing the first element during the first pass makes the loop see 1, 3 and 4, and never 2.

    Common mistakes

    • Pushing to the wrong array. The two names are similar, and a loop over one with push on the other is easy to type by accident. Read the header and the body together.
    • Expecting the loop to use the starting size. It does not remember how long the array was when it began.
    • Testing a possibly infinite loop on real data. If you suspect a loop might not end, try it with a small guard, for instance a counter that stops with break after a few passes.
    • Thinking const prevents this. const stops you from reassigning the variable, not from changing the array it refers to, so push is still allowed.

    Key takeaways

    • A for...of loop on an array ends only when it reaches the end of the array as it currently is.
    • Adding an element to the array in its own loop keeps the end out of reach, and the loop never finishes.
    • Read from one array and write to another, or loop over a copy.
    • Removing elements during a loop can skip elements.
    Keep going with TypeScriptAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free