What a nested loop is
A nested loop is a loop written inside the body of another loop. The outer loop starts a pass, the inner loop runs through all of its passes, and only when the inner loop is finished does the outer loop move on to its next pass. Each time the outer loop starts a new pass, the inner loop starts again from the beginning.
If the outer loop runs 5 times and the inner loop runs 10 times for each of them, the inner body runs 5 × 10 = 50 times. This multiplication is the main thing to remember about nested loops, and it is why they matter for performance later in the course.
Nested loops are the natural tool whenever data has two dimensions: rows and columns, a grid, a table, or every pair of elements in a list.
The lesson's exercise: a rectangle of stars
The task is to print a rectangle of 5 rows and 10 columns made of * characters. The outer loop counts rows and the inner loop counts the stars within one row.
- Write the outer loop.
for (let row = 0; row < 5; row++)runs once per row. The body never readsrow; it only counts the passes. - Write the inner loop inside it.
for (let col = 0; col < 10; col++)runs once per column of the current row. - Print one star per pass of the inner loop with
process.stdout.write("*"). This call prints exactly the text you give it, with nothing added, so the 10 stars of a row land on the same line. - End the row. After the inner loop, still inside the outer loop, print a newline with
process.stdout.write("\n"). The next row then starts on a new line.
for (let row = 0; row < 5; row++) {
for (let col = 0; col < 10; col++) {
process.stdout.write("*");
}
process.stdout.write("\n");
}
The output:
**********
**********
**********
**********
**********
Why not console.log("*")? It always ends its output with a newline, so every star would be alone on its own line. process.stdout.write leaves that decision to you, which is what makes it possible to build a line in pieces. The position of the newline matters too. If you put it inside the inner loop, you get 50 lines with one star each. After the inner loop but outside the outer one, you get one line of 50 stars and a single newline at the end.
Using the row number
The inner loop can depend on the outer loop's variable. This version prints a triangle, because row row gets row stars:
for (let row = 1; row <= 4; row++) {
for (let col = 0; col < row; col++) {
process.stdout.write("*");
}
process.stdout.write("\n");
}
*
**
***
****
Here the inner loop doesn't always run the same number of times: 1, then 2, then 3, then 4. The total is no longer a simple product, but the structure is the same.
Looping over a two-dimensional array
A two-dimensional array is an array whose elements are arrays. Its type is written number[][]. The outer loop picks a row, and the inner loop walks along that row:
const arr: number[][] = [
[1, 2], [3, 4], [5, 6]
];
let total = 0;
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr[i].length; j++) {
total += arr[i][j];
}
}
console.log(total);
21
Notice the inner loop's bound: arr[i].length, not arr.length. The element arr[i] is itself an array, and the rows of a TypeScript array of arrays are allowed to have different lengths, so each row gets asked for its own.
break only leaves one loop
A break stops the innermost loop that contains it. Placed in the inner loop, it ends only that loop, and the outer loop carries on:
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]);
}
}
1
2
5
6
When the inner loop reaches the 3, it stops. The 4 is skipped, but the outer loop moves on to the row [5, 6]. If the break sits in the outer loop, outside the inner one, everything stops:
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]);
}
}
1
2
To leave both loops from inside the inner one, put a label in front of the outer loop and name it in the break:
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]);
}
}
1
2
Inside a function, return is often a cleaner way to leave nested loops, since it ends the whole function at once.
Common mistakes
- Reusing the same variable name. If both loops use
i, the innerlet ihides the outer one inside its own body, which makes the code confusing. Use distinct names such asrowandcol, oriandj. - Putting the newline in the wrong place. It belongs after the inner loop and inside the outer one.
- Using the wrong length. In a loop over a two-dimensional array, the inner bound is
arr[i].length. - Expecting
breakto leave every loop. It ends only the innermost one. - Underestimating the cost. Two nested loops over
nitems run their body aboutn * ntimes. A thousand items means a million passes.
Key takeaways
- The inner loop restarts and finishes completely on every pass of the outer loop.
- The inner body runs (outer passes) × (inner passes) times.
- Rows go in the outer loop and columns in the inner one; the newline goes between the two.
breakleaves only the innermost loop unless you use a label or areturn.