Start learning free

LESSON 184 OF 410 · NESTED LOOPS

Nested Loops in JavaScript: for, while and break

Lesson Read the full explanation

A nested loop has one loop inside of another. These are typically used for working with two dimensions such as printing stars in rows and columns as shown below. When a loop is nested inside another loop, the inner loop runs many times inside the outer loop. In each iteration of the outer loop, the inner loop will be re-started. The inner loop must finish all of its iterations before the outer loop can continue to its next iteration.

Example:

for(let row = 0; row < 3; row++) {
	for(let col = 0; col < 5; col++) {
		process.stdout.write("*");
	}
    process.stdout.write('\n');
}

// This code prints a rectangle with 3 rows and 5 columns, filled with '*'
// We use process.stdout.write because console.log prints with a trailing newline

Iterating through multidimensional arrays

Nested loops are also helpful when we want to iterate through a multi dimensional array, for example:

const arr = [
	[1, 2], [3, 4], [5, 6]
];

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

This outputs each sub-element in arr one at a time. Note that for the inner loop, we are checking the .length of arr[i], since arr[i] is itself an array.

Breaking nested loops

In a nested loop, a break statement only stops the loop it is placed in. Therefore, if a break is placed in the inner loop, the outer loop still continues. For example:

const arr = [
	[1, 2], [3, 4], [5, 6]
];

for (let i = 0; i < arr.length; i++) {
	for (let j = 0; j < arr[i].length; j++) {
		if(arr[i][j] == 3) {
			break;
		}
		console.log(arr[i][j]);
	}
}

// prints the numbers 1, 2, 5 and 6 on different lines

When i is 1 and j is 0, we execute the break. This means we stop the inner loop and go back to the outer loop to continue from the next iteration, which is i = 2. And as you can see, we print all the elements of row 2.

However, if the break is placed in the outer loop, all of the looping stops. For example:

const arr = [
	[1, 2], [3, 4], [5, 6]
];

for (let i = 0; i < arr.length; i++) {
	if(arr[i][0] == 3) {
		break;
	}
	for (let j = 0; j < arr[i].length; j++) {
		console.log(arr[i][j]);
	}
}
// prints the numbers 1 and 2 on different lines

Assignment
Follow the Coding Tutorial and let's write some nested loops.


Hint
Look at the examples above if you get stuck.


STEP 1 OF 4
    Ctrl + Enter to check

    JAVASCRIPT · NESTED LOOPS

    Nested Loops in JavaScript, explained

    What a nested loop is

    A nested loop is a loop placed inside the body of another loop. The outer loop starts a pass, the inner loop then runs from its first iteration to its last, and only after that does the outer loop move to its next pass. Every pass of the outer loop restarts the inner loop from scratch.

    You reach for nested loops whenever the data or the output has two dimensions: rows and columns of a grid, an array of arrays, or every pair of items in a list. Nested for loops are the most common form, but any loop can sit inside any other, including while inside for and for...of inside for.

    A nested for loop, step by step

    Here is the shape of the lesson's exercise, a rectangle of stars with 5 rows and 10 columns. The outer loop counts rows and the inner loop counts columns:

    for (let row = 0; row < 5; row++) {
        for (let col = 0; col < 10; col++) {
            process.stdout.write("*");
        }
        process.stdout.write("\n");
    }
    
    **********
    **********
    **********
    **********
    **********
    

    To build it:

    1. Write the outer loop with a variable row that starts at 0 and runs while row < 5. It makes one pass for each row. Its body does not read row, which is fine: the variable only counts the passes.
    2. Write the inner loop inside it with col running from 0 while col < 10. It makes one pass for each column of the current row.
    3. Print one star per inner pass with process.stdout.write("*"). Unlike console.log, which always ends its output with a newline, process.stdout.write prints exactly the text it is given, so the 10 stars of a row stay on one line.
    4. Print the newline after the inner loop, still inside the outer loop. Put it inside the inner loop and every star lands on its own line; put it after the outer loop and there is a single line break at the very end.

    The inner body runs once per pass of the outer loop times once per pass of the inner loop, so here the star line runs 5 × 10 = 50 times. This multiplication is also how you estimate the cost of nested loops: two loops that each run 100 times execute the innermost line 10,000 times.

    process.stdout.write exists in Node.js. In a browser console, build each row as a string and print it once with console.log:

    for (let row = 1; row <= 4; row++) {
      let line = "";
      for (let col = 0; col < row; col++) line += "*";
      console.log(line);
    }
    
    *
    **
    ***
    ****
    

    This one also shows that the inner loop's limit can depend on the outer variable. The inner loop runs row times, so each row is longer than the one before.

    Looping over a two-dimensional array

    A grid in JavaScript is an array whose items are arrays. The outer loop picks a row; the inner loop walks that row. The inner limit is arr[i].length, the length of the current row, not arr.length:

    const arr = [
        [1, 2], [3, 4], [5, 6]
    ];
    
    for (let i = 0; i < arr.length; i++) {
        for (let j = 0; j < arr[i].length; j++) {
            console.log(arr[i][j]);
        }
    }
    

    This prints 1, 2, 3, 4, 5 and 6, one per line. When you only need the values and not the positions, for...of is shorter and removes the chance of an off-by-one error:

    for (const row of arr) {
        for (const value of row) {
            console.log(value);
        }
    }
    

    The same nesting works with forEach callbacks, but a break or continue is not allowed inside one, because the body is a function and not a loop. return in a callback only leaves that one call. Use for or for...of when you need to stop early.

    Using both loop variables

    The two variables together give you a position, which is how a multiplication table is built:

    for (let i = 1; i <= 3; i++) {
      let line = "";
      for (let j = 1; j <= 3; j++) line += (i * j) + " ";
      console.log(line.trim());
    }
    
    1 2 3
    2 4 6
    3 6 9
    

    Starting the inner loop at i + 1 instead of 0 visits every pair of items once, without pairing an item with itself or repeating a pair in the other order. With nums = [1, 2, 3] and j starting at i + 1, the pairs are 1 2, 1 3 and 2 3.

    Breaking out of nested loops

    A break stops only the loop it is written in. Placed in the inner loop, it ends the inner loop and the outer loop carries on with its next pass:

    for (let i = 0; i < arr.length; i++) {
        for (let j = 0; j < arr[i].length; j++) {
            if (arr[i][j] == 3) {
                break;
            }
            console.log(arr[i][j]);
        }
    }
    

    This prints 1, 2, 5 and 6. When the inner loop reaches 3 (at i = 1, j = 0) it stops, so 4 is skipped, and the outer loop goes on to i = 2 and prints the whole last row.

    A break in the outer loop, outside the inner one, ends everything. This version checks the first value of each row before the inner loop starts:

    for (let i = 0; i < arr.length; i++) {
        if (arr[i][0] == 3) {
            break;
        }
        for (let j = 0; j < arr[i].length; j++) {
            console.log(arr[i][j]);
        }
    }
    

    It prints 1 and 2 only, because the second row starts with 3 and the outer loop ends there.

    Leaving both loops at once

    To stop all the looping from inside the inner loop, give the outer loop a label and break to it:

    outer: for (let i = 0; i < arr.length; i++) {
        for (let j = 0; j < arr[i].length; j++) {
            if (arr[i][j] === 3) break outer;
            console.log(arr[i][j]);
        }
    }
    

    This prints 1 and 2. Inside a function you can also use return, which leaves every loop at once and is often the clearer choice. continue follows the same rule as break: it skips to the next pass of the loop it is in, so in the inner loop it skips the rest of one inner pass.

    Common mistakes

    • Reusing the same variable name. If the inner loop declares its own let i the code still runs, because the inner i hides the outer one. But the outer value is out of reach inside the inner loop, which is nearly always a bug. Use different names such as row and col, or i and j.
    • Using var in the loops. A var variable belongs to the whole function, so an inner loop that reuses the name of an outer var changes the outer counter and can make the outer loop misbehave. Declare loop counters with let.
    • Using the wrong length for the inner bound. For rows of different lengths, arr.length in the inner loop reads past the end of short rows and gives undefined. Use arr[i].length.
    • Putting the newline in the wrong place. A newline inside the inner loop breaks every row into single characters. After the outer loop it joins all rows into one.
    • Expecting break to leave both loops. It leaves only the innermost one. Use a label or return to leave both.
    • Ignoring the cost. Nested loops over the same input of size n run about n * n times. That is fine for small inputs and slow for large ones, so look for a simpler approach when the input can be big.

    Key takeaways

    • The inner loop runs completely for every pass of the outer loop.
    • The total work is the outer count times the inner count.
    • For a 2D array, loop over rows first, and bound the inner loop by the current row's length.
    • break and continue affect only the loop that contains them. Use a label or return to leave both loops.
    • Give the loops different variable names and declare them with let.
    Keep going with JavaScriptAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free