Nested loops and pattern programs
A nested loop is a loop inside another loop. The inner loop runs completely for every single iteration of the outer one, which makes nested loops perfect for anything with rows and columns: grids, tables and patterns.
How a nested loop runs
for (let row = 1; row <= 3; row++) {
for (let col = 1; col <= 4; col++) {
console.log(`row ${row}, column ${col}`);
}
}The outer loop runs 3 times and the inner loop 4 times per row, so the inner body runs 3 × 4 = 12 times. Think of it like a clock: the minute hand (inner loop) goes all the way round before the hour hand (outer loop) moves once.
Building a row, then printing it
console.log always ends its line, so for a pattern you build each row as a string in the inner loop and print it once the row is finished:
for (let row = 1; row <= 3; row++) {
let line = "";
for (let col = 1; col <= 5; col++) {
line += "* ";
}
console.log(line);
}The outer loop chooses the row; the inner loop fills that row's columns.
A times-table grid
for (let i = 1; i <= 5; i++) {
let line = "";
for (let j = 1; j <= 5; j++) {
line += String(i * j).padStart(4); // pad to 4 characters so the columns line up
}
console.log(line);
}Pattern 1: a right-angled triangle
The trick in every pattern is how the inner loop's limit depends on the row. Here row 1 gets 1 star, row 2 gets 2, and so on: the inner loop runs row times.
const n = 5;
for (let row = 1; row <= n; row++) {
let line = "";
for (let col = 1; col <= row; col++) {
line += "* ";
}
console.log(line);
}Pattern 2: the triangle upside down
Count the rows down instead, and the triangle flips:
const n = 5;
for (let row = n; row >= 1; row--) {
let line = "";
for (let col = 1; col <= row; col++) {
line += "* ";
}
console.log(line);
}Pattern 3: a number triangle
Print the column number instead of a star:
const n = 5;
for (let row = 1; row <= n; row++) {
let line = "";
for (let col = 1; col <= row; col++) {
line += col + " ";
}
console.log(line);
}Print row instead of col and every row repeats its own number (1, 2 2, 3 3 3, ...). Try it.
Pattern 4: a pyramid
A pyramid needs two inner loops per row: one for the spaces that push the stars to the middle, then one for the stars. Row r of an n-row pyramid has n - r spaces and 2r - 1 stars:
const n = 5;
for (let row = 1; row <= n; row++) {
let line = "";
for (let s = 1; s <= n - row; s++) {
line += " ";
}
for (let star = 1; star <= 2 * row - 1; star++) {
line += "*";
}
console.log(line);
}| row | spaces (n - row) | stars (2 × row - 1) |
|---|---|---|
| 1 | 4 | 1 |
| 2 | 3 | 3 |
| 3 | 2 | 5 |
| 4 | 1 | 7 |
| 5 | 0 | 9 |
Pattern 5: Floyd's triangle
A counter that lives outside both loops keeps counting across rows:
const n = 5;
let num = 1;
for (let row = 1; row <= n; row++) {
let line = "";
for (let col = 1; col <= row; col++) {
line += num + " ";
num++;
}
console.log(line);
}A shortcut: repeat()
For simple patterns, a string's repeat() method replaces the inner loop. It's handy once you understand what the inner loop does:
const n = 5;
for (let row = 1; row <= n; row++) {
console.log(" ".repeat(n - row) + "*".repeat(2 * row - 1));
}Breaking out of both loops
break only leaves the innermost loop. To stop both, give the outer loop a label:
outer: for (let i = 1; i <= 3; i++) {
for (let j = 1; j <= 3; j++) {
if (i * j === 4) {
console.log(`found 4 at i = ${i}, j = ${j}`);
break outer;
}
}
}
console.log("done");