Part 1 · Lesson 6 · 20 min read
Nested Loops and Patterns
A loop inside a loop is a grid: rows outside, columns inside. Built up with star and number patterns until you can draw any shape.
On this page
A loop inside a loop is a grid
A nested loop is a loop that has another loop inside its body. The outer loop is the first one. The inner loop is the one inside it. One rule makes nested loops easy to predict: for every single iteration of the outer loop, the inner loop runs completely, from start to finish.
Picture a grid. The outer loop picks a row. The inner loop walks along every column of that row. When the row is done, the outer loop moves down one row, and the inner loop starts again from the first column. This is why the outer variable is usually called i (row) and the inner one j (column).
Patterns (stars, numbers, triangles) are a common way to practise this. Companies often ask them in campus and first-round interviews, because they test whether you can control two loops at once. After this lesson, comparing every pair of items in an array (Lessons 8 and 21) will feel natural.
The clock picture
A clock works like a nested loop. The hour hand is the outer loop. The minute hand is the inner loop. For every one step of the hour hand, the minute hand goes all the way round (60 steps) and then starts again from 0.
Traced: every (row, column) pair
The outer loop starts row 1.
▶for (let i = 1; i <= 2; i++) {2 for (let j = 1; j <= 3; j++) {3 console.log(i, j);4 }5}| i | 1 |
| i (row) | j values in this row | Printed |
|---|---|---|
| 1 | 1, 2, 3 | 1 1 · 1 2 · 1 3 |
| 2 | 1, 2, 3 | 2 1 · 2 2 · 2 3 |
Total prints: 2 × 3 = 6.
Building one row, then printing it
console.log always ends the line, so you cannot print stars one by one on the same line. Do this instead. Start each row with an empty string (a string with no characters, written as ""). Let the inner loop add characters to it. Print the row when it is complete.
const N = 3;for (let i = 1; i <= N; i++) { // one iteration per ROW let row = ""; // a fresh, empty row each time for (let j = 1; j <= N; j++) { // one iteration per COLUMN row += "*"; // add one star to this row } console.log(row); // print the finished row — AFTER the inner loop}/* Output:**********/let row = ""goes inside the outer loop, before the inner one. Then every row starts empty.console.log(row)goes inside the outer loop, after the inner one. Then it prints once for each row.- If you put the print inside the inner loop, you get one line for every star instead.
The three-question recipe for any pattern
- How many rows? That is the outer loop:
ifrom 1 to N. - In row i, what is printed, and how many of it? Write it down for rows 1, 2, 3 and 4. Then look for a rule that uses i, such as “i stars”, “N − i + 1 stars” or “the numbers 1 to i”.
- Build it: the inner loop (or loops) follows that rule. Then print the row.
| Row i | Looks like | Stars in the row |
|---|---|---|
| 1 | * | 1 |
| 2 | ** | 2 |
| 3 | *** | 3 |
| 4 | **** | 4 |
Stars = i. So the inner loop is for (let j = 1; j <= i; j++). Its stopping point depends on the outer variable.
How many times does the inside run?
let count = 0;for (let i = 1; i <= 4; i++) { for (let j = 1; j <= 4; j++) { count++; }}console.log(count); // 16 (4 rows × 4 columns)A loop of N inside a loop of N runs its body N × N times. For N = 1,000 that is a million iterations. Keep this in mind. In Lesson 12 it is called O(n²) (“order n squared”), a way to say that the work grows with the square of the input size. A big part of DSA is learning tricks to avoid doing all N × N steps.
Practice: 11 patterns and grids
For each one, answer the three recipe questions on paper first. Most examples use N = 4.
Print an N × N square of stars.
**** **** **** ****
Hint 1
Hint 2
Show all 2 solutions with explanations
The outer loop makes N rows. For each row, the inner loop adds N stars, then the row is printed.
const N = 4;for (let i = 1; i <= N; i++) { let row = ""; for (let j = 1; j <= N; j++) { row += "*"; } console.log(row);}/* Output:*****************/The inner count (N) does not depend on i, so every row is the same. This is the template (the basic shape to copy) for every pattern below.
Every row is identical, so make it once with "*".repeat(N) and print it N times.
const N = 4;const row = "*".repeat(N);for (let i = 1; i <= N; i++) { console.log(row);}/* Output:*****************/text.repeat(k) is a string method (a function that belongs to text) that returns the text repeated k times. Building the row once avoids doing the same work in every row.
Write Approach 1 while you are learning. Interviewers who ask pattern questions want to see the two loops. Use repeat in everyday code.
Row i has i stars.
* ** *** ****
Hint 1
i?Hint 2
i has i stars, so the inner loop runs while j <= i.Show all 3 solutions with explanations
This is the same as the square, with one change: the inner loop stops at i instead of N.
const N = 4;for (let i = 1; i <= N; i++) { let row = ""; for (let j = 1; j <= i; j++) { row += "*"; } console.log(row);}/* Output:***********/The key to every triangle is this: the stopping point of the inner loop depends on the outer variable.
Each row is the previous row plus one star. Keep the row from one iteration to the next, and add one star each time.
const N = 4;let row = "";for (let i = 1; i <= N; i++) { row += "*"; // one more star than last time console.log(row);}/* Output:***********/Here row is declared outside the loop, so it keeps its stars from the previous iteration. Only one loop is needed.
Row i is simply "*".repeat(i).
const N = 4;for (let i = 1; i <= N; i++) { console.log("*".repeat(i));}/* Output:***********/This is the shortest version. The inner loop is hidden inside repeat.
Approach 1 is the one to understand. Approach 2 shows a useful idea. When each row extends the previous one, keep the previous row instead of building it again.
Row i shows the numbers 1 to i.
1 1 2 1 2 3 1 2 3 4
Hint 1
Hint 2
j. Remove the extra space at the end with trimEnd().Show all 2 solutions with explanations
Use the same loops as the triangle, but add j and a space instead of a star.
const N = 4;for (let i = 1; i <= N; i++) { let row = ""; for (let j = 1; j <= i; j++) { row += j + " "; } console.log(row.trimEnd()); // remove the space after the last number}/* Output:11 21 2 31 2 3 4*/trimEnd() removes spaces at the end of the text. Some online judges (websites that check your answer automatically) compare output character by character. For them, a space at the end of a line can make a correct answer fail.
Each row is the previous row with the next number added.
const N = 4;let row = "";for (let i = 1; i <= N; i++) { row += (i === 1 ? "" : " ") + i; // add a space before every number except the first console.log(row);}/* Output:11 21 2 31 2 3 4*/Adding the space before each number (except the first) avoids a space at the end, so you do not need trimEnd.
Both are correct. Approach 1 matches the general recipe, and it is easier to change for other patterns.
Row i shows the number i, i times.
1 2 2 3 3 3 4 4 4 4
Hint 1
i or the column j?Show all 2 solutions with explanations
This is question 3 with one letter changed. Print i instead of j.
const N = 4;for (let i = 1; i <= N; i++) { let row = ""; for (let j = 1; j <= i; j++) { row += i + " "; } console.log(row.trimEnd());}/* Output:12 23 3 34 4 4 4*/Pattern questions train you to say whether a value depends on the row or on the column.
Repeat the small piece of text "i " (the number i and a space) i times. Then remove the final space.
const N = 4;for (let i = 1; i <= N; i++) { console.log((i + " ").repeat(i).trimEnd());}/* Output:12 23 3 34 4 4 4*/(i + " ") is text like "3 ". Repeating it 3 times gives "3 3 3 ".
Row 1 has N stars, and each row after it has one star fewer.
**** *** ** *
Hint 1
Hint 2
Show all 2 solutions with explanations
Let the rows count up, and give row i exactly N − i + 1 stars.
const N = 4;for (let i = 1; i <= N; i++) { let row = ""; for (let j = 1; j <= N - i + 1; j++) { row += "*"; } console.log(row);}/* Output:***********/| i | N − i + 1 |
|---|---|
| 1 | 4 |
| 2 | 3 |
| 3 | 2 |
| 4 | 1 |
Let i go from N down to 1 and print i stars.
const N = 4;for (let i = N; i >= 1; i--) { let row = ""; for (let j = 1; j <= i; j++) { row += "*"; } console.log(row);}/* Output:***********/This is the triangle from question 2, with the outer loop reversed. It is often the easiest way to turn a pattern upside down.
Both are correct. Pick the rule that you can explain most easily. Approach 2 is usually quicker to write.
Print a pyramid of stars, with each row centred.
* *** ***** *******
Hint 1
Hint 2
i for each column of the table.Hint 3
Show all 3 solutions with explanations
For each row, first add N − i spaces, then 2i − 1 stars.
const N = 4;for (let i = 1; i <= N; i++) { let row = ""; for (let s = 1; s <= N - i; s++) row += " "; // spaces for (let k = 1; k <= 2 * i - 1; k++) row += "*"; // stars console.log(row);}/* Output: * *** *************/| i | spaces = N − i | stars = 2i − 1 |
|---|---|---|
| 1 | 3 | 1 |
| 2 | 2 | 3 |
| 3 | 1 | 5 |
| 4 | 0 | 7 |
The pyramid fits in a grid that is 2N − 1 columns wide. A cell gets a star if its distance from the centre column is less than i. Otherwise it gets a space.
const N = 4;const width = 2 * N - 1;const centre = N; // columns are numbered 1..widthfor (let i = 1; i <= N; i++) { let row = ""; for (let j = 1; j <= width; j++) { row += Math.abs(j - centre) < i ? "*" : " "; } console.log(row.trimEnd());}/* Output: * *** *************/Math.abs(j - centre) is how far column j is from the centre (Math.abs removes a minus sign, so the distance is never negative). In row 1, only the centre itself (distance 0) gets a star. In row 4, every column with a distance of up to 3 gets one.
Build each row from two repeated pieces of text: spaces and stars.
const N = 4;for (let i = 1; i <= N; i++) { console.log(" ".repeat(N - i) + "*".repeat(2 * i - 1));}/* Output: * *** *************/These are the same formulas as in Approach 1, but you do not write the inner loops yourself.
Approach 1 is the standard answer. Approach 2 is a powerful general technique. For any shape, ask: “for cell (i, j), what belongs here?”
Print the border of an N × N square, with spaces inside.
**** * * * * ****
Hint 1
Hint 2
Show all 2 solutions with explanations
Loop over every (row, column) cell and print a star only on the border.
const N = 4;for (let i = 1; i <= N; i++) { let row = ""; for (let j = 1; j <= N; j++) { const onBorder = i === 1 || i === N || j === 1 || j === N; row += onBorder ? "*" : " "; } console.log(row);}/* Output:***** ** ******/This “what belongs in cell (i, j)?” approach works for every pattern. You will use it for matrices (grids of numbers) in Lesson 25.
There are only two kinds of row: a full row of stars, and a row with a star at each end and spaces in between.
const N = 4;const full = "*".repeat(N);const middle = N === 1 ? "*" : "*" + " ".repeat(N - 2) + "*";for (let i = 1; i <= N; i++) { console.log(i === 1 || i === N ? full : middle);}/* Output:***** ** ******/Each kind of row is built once. The check for N === 1 handles an edge case (an unusual input). With N = 1, N - 2 would be −1, and repeat would throw an error.
Approach 1 is the general method, and it handles N = 1 by itself. Approach 2 is faster to run, but you must think about the edge case.
Fill a right triangle with consecutive numbers (1, 2, 3, and so on) starting at 1.
1 2 3 4 5 6 7 8 9 10
Hint 1
Hint 2
Show all 2 solutions with explanations
Print the counter in every cell. Increase it after each number.
const N = 4;let num = 1; // lives outside both loopsfor (let i = 1; i <= N; i++) { let row = ""; for (let j = 1; j <= i; j++) { row += num + " "; num++; } console.log(row.trimEnd());}/* Output:12 34 5 67 8 9 10*/If num were declared inside the outer loop, it would go back to 1 in every row. The place where you declare a variable decides how long it keeps its value.
Before row i there are 1 + 2 + … + (i − 1) = i(i − 1)/2 numbers. So row i starts at i(i − 1)/2 + 1.
const N = 4;for (let i = 1; i <= N; i++) { const start = (i * (i - 1)) / 2 + 1; let row = ""; for (let j = 0; j < i; j++) { row += start + j + " "; } console.log(row.trimEnd());}/* Output:12 34 5 67 8 9 10*/Row 4 starts at 4 × 3 / 2 + 1 = 7. This version can print any single row without working out the rows before it.
Approach 1 is the simplest. Approach 2 uses the sum formula from Lesson 4. It is useful if you need only one row.
Print a triangle of alternating 1s and 0s, as in the example.
1 0 1 1 0 1 0 1 0 1
Hint 1
Hint 2
(i + j) % 2 === 0, otherwise 0.Show all 2 solutions with explanations
Cell (i, j) holds 1 when i + j is even and 0 when it is odd.
const N = 4;for (let i = 1; i <= N; i++) { let row = ""; for (let j = 1; j <= i; j++) { row += ((i + j) % 2 === 0 ? 1 : 0) + " "; } console.log(row.trimEnd());}/* Output:10 11 0 10 1 0 1*/Cell (1,1): 1 + 1 = 2, which is even, so the value is 1. Cell (2,1): 3 is odd, so the value is 0. Cell (2,2): 4 is even, so the value is 1. It is the same rule that colours a chessboard.
Odd rows start with 1 and even rows start with 0. After each number, flip the value (1 becomes 0, and 0 becomes 1).
const N = 4;for (let i = 1; i <= N; i++) { let value = i % 2 === 1 ? 1 : 0; let row = ""; for (let j = 1; j <= i; j++) { row += value + " "; value = 1 - value; // flip } console.log(row.trimEnd());}/* Output:10 11 0 10 1 0 1*/1 - value turns 1 into 0 and 0 into 1. The code follows the pattern in the same way as you would describe it in words.
Both are correct. Approach 1 shows the habit of looking for a formula that uses (i, j). Approach 2 is easier to find by looking at the output.
How many pairs (i, j) are there with 1 ≤ i < j ≤ N?
Hint 1
Hint 2
i + 1. Then j is always bigger than i, and no pair is counted twice.Show all 2 solutions with explanations
For each i, count every j from i + 1 to N.
function countPairs(N) { let count = 0; for (let i = 1; i <= N; i++) { for (let j = i + 1; j <= N; j++) { count++; } } return count;}console.log(countPairs(4)); // 6console.log(countPairs(5)); // 10This is the shape of every brute-force “compare every pair” solution. Brute force means trying every possibility. An example is the first approach to Two Sum (Lesson 26).
Number 1 pairs with N − 1 others, number 2 with N − 2 others, and so on. The total is (N − 1) + … + 1 = N(N − 1)/2.
function countPairs(N) { return (N * (N - 1)) / 2;}console.log(countPairs(4)); // 6console.log(countPairs(5)); // 10This is the sum formula from Lesson 4 again. The count grows like N², which is a hint that comparing every pair becomes very slow for large N.
If you only need the count, use the formula. If you need to do something with each pair, you need the loops. This is why Part 4 teaches ways to avoid checking every pair.
Print the first N rows of Pascal's triangle.
1 1 1 1 2 1 1 3 3 1 1 4 6 4 1
Hint 1
Hint 2
Show all 2 solutions with explanations
Keep the previous row as an array (a list). For the new row, add neighbouring pairs from the previous row, and put 1 at both ends.
const N = 5;let prev = [];for (let i = 0; i < N; i++) { const row = [1]; for (let j = 1; j < i; j++) { row.push(prev[j - 1] + prev[j]); // the two numbers above } if (i > 0) row.push(1); console.log(row.join(" ")); prev = row;}/* Output:11 11 2 11 3 3 11 4 6 4 1*/This uses arrays from Lesson 8. push adds an item to the end. prev[j] reads the item at a position. join(" ") turns the row into text. This is LeetCode 118.
The numbers in row i are “i choose 0”, “i choose 1” and so on. “i choose k” is the number of ways to pick k items from i items. Each number can be worked out from the one before it: next = current × (i − k) / (k + 1).
const N = 5;for (let i = 0; i < N; i++) { let value = 1; let row = ""; for (let k = 0; k <= i; k++) { row += value + " "; value = (value * (i - k)) / (k + 1); } console.log(row.trimEnd());}/* Output:11 11 2 11 3 3 11 4 6 4 1*/For row 4 (counting rows from 0, so this is the fifth row): 1, then 1 × 4 / 1 = 4, then 4 × 3 / 2 = 6, then 6 × 2 / 3 = 4, then 4 × 1 / 4 = 1. You do not need to store the previous row.
Approach 1 is the answer interviewers expect, and it is easier to explain. Approach 2 is a short maths option that needs no extra list.
Make it stick
- Say the rule out loud: “for every iteration of the outer loop, the inner loop runs…”
- Without looking, write the pyramid for N = 3 and its table of spaces and stars.
- Explain where
let row = ""andconsole.log(row)must go, and what goes wrong if either moves.
Revisit schedule: solve two of the questions again tomorrow, again in 3 days, and once more in a week — without looking at the answers. Each time it will feel easier. That is the memory forming.
Next lesson: functions. A function packs code into a named block so you can reuse it. It is also the format of every interview answer.
Practise on LeetCode
Real interview problems that use exactly what this lesson teaches. Solve them in JavaScript after the practice questions above.
Published by Jainik · Last updated · How these lessons are made
Keep learning
Related series