Part 1 · Lesson 4 · 22 min read
The for Loop
The most important idea in this series. Start, condition and update, traced step by step with a dry-run table until the pattern is clear.
On this page
- Why loops exist
- The three parts of a for loop
- The exact order things happen
- Traced: printing 1 to 3
- How many times will it run?
- Counting down, skipping, starting at 0
- The accumulator: adding things up
- Traced: the sum of 1 to 4
- break and continue
- Off-by-one and other loop bugs
- A recipe for writing any loop
- Practice questions (10)
- Make it stick
Why loops exist
Print the numbers from 1 to 5. With what you know so far:
console.log(1);console.log(2);console.log(3);console.log(4);console.log(5);// ...and for 1 to 1000? 1000 lines. There has to be a better way.A loop is a statement that repeats a block of code again and again, and stops when a condition is no longer true. Each time the block runs is called an iteration. Here is the same output in three lines:
for (let i = 1; i <= 5; i++) { console.log(i);}/* Output:12345*/Change 5 to 1000 and it prints a thousand lines, but the code is still three lines long. Nearly every DSA solution has at least one loop. So this lesson is the base for everything after it. Take your time and trace every example.
The three parts of a for loop
A for loop is a loop that keeps all its counting in one place. Inside the round brackets it has three parts, separated by semicolons: start, check and update. After the brackets comes the body (the code to repeat) in curly braces. The table lists all four steps in the order you read them in the code.
for (let i = 1; i <= 5; i++) {// ───────── ────── ───// 1. START 2. CHECK 4. UPDATE// runs once before after every// at the every iteration// beginning iteration console.log(i); // 3. BODY — runs only while CHECK is true}| Part | Here | Job |
|---|---|---|
| 1. Start | let i = 1 | Create the loop variable. Runs once. |
| 2. Check | i <= 5 | Asked before every iteration. If true, run the body. If false, leave the loop. |
| 3. Body | console.log(i) | The work to repeat. Can use i. |
| 4. Update | i++ | Runs after every iteration. It moves i closer to the stopping point. |
The variable is usually called i (short for “index”). It is the loop variable: the counter that changes in every iteration and decides when the loop stops.
The exact order things happen
This is the most important part to remember. For for (start; check; update) { body }, the order is:
start → check → body → update → check → body → update → check → … → check (false) → exit
- Start happens once, before anything else.
- Check happens one more time than the body. The last check fails, and that failing check ends the loop.
- Update always comes after the body, never before.
Traced: printing 1 to 3
Press Next, and say each step out loud before you see it. Watch the order of check, body and update:
Runs exactly once. The loop variable i is created with value 1.
▶for (let i = 1; i <= 3; i++) {2 console.log(i);3}4console.log("done");| i | 1 |
| Iteration | i at check | i <= 3? | Prints | i after update |
|---|---|---|---|---|
| 1 | 1 | true | 1 | 2 |
| 2 | 2 | true | 2 | 3 |
| 3 | 3 | true | 3 | 4 |
| — | 4 | false | (loop ends) | — |
Three iterations of the body, four checks. After the loop, i is 4, which is one more than the last value printed.
How many times will it run?
You will be asked this often. It is also how you will later work out Big-O (Lesson 12), a way to describe how the running time of code grows with the input size. Three forms cover almost everything:
| Loop | i takes the values | Runs |
|---|---|---|
for (let i = 1; i <= n; i++) | 1, 2, …, n | n times |
for (let i = 0; i < n; i++) | 0, 1, …, n − 1 | n times |
for (let i = a; i <= b; i++) | a, a + 1, …, b | b − a + 1 times |
The second form (start at 0, use <) may look strange now. But it is the most common form in DSA, because arrays (lists of values) and strings number their items from 0 (Lesson 8).
Counting down, skipping, starting at 0
Change any of the three parts to change the pattern of values:
// Count down: start high, check >=, update with --for (let i = 5; i >= 1; i--) console.log(i); // 5 4 3 2 1 (one per line)// Every second number: update with += 2for (let i = 2; i <= 10; i += 2) console.log(i); // 2 4 6 8 10// Run exactly n times, counting from 0 — the most common form in DSAconst n = 3;for (let i = 0; i < n; i++) console.log("hello"); // hello ×3 (i is 0, 1, 2)When the body is one statement, you may leave out the braces, as above. While you are learning, keep the braces. Then you will not make a common mistake when you add a second line later (the second line would not be part of the loop).
The accumulator: adding things up
Printing is useful, but most loop problems ask for one answer built from many steps, such as a total, a count or a product. The pattern always has the same three moves:
- Before the loop, create a variable for the answer and give it a starting value. Use
0for a sum or a count. Use1for a product. - Inside the loop, update it using the value of this iteration.
- After the loop, the variable holds the answer.
That variable is called an accumulator (a variable that collects a result step by step, like a jar where you keep adding coins). It must be declared outside the loop. If you declare it inside, it is reset to 0 in every iteration.
Traced: the sum of 1 to 4
The accumulator starts empty. 0 means “nothing added yet”. It lives OUTSIDE the loop so it keeps its value between iterations.
▶let sum = 0;2for (let i = 1; i <= 4; i++) {3 sum = sum + i;4}5console.log(sum);| sum | 0 |
| i | sum before | sum = sum + i | sum after |
|---|---|---|---|
| 1 | 0 | 0 + 1 | 1 |
| 2 | 1 | 1 + 2 | 3 |
| 3 | 3 | 3 + 3 | 6 |
| 4 | 6 | 6 + 4 | 10 |
| 5 | — | check fails, loop ends | 10 |
break and continue
Two keywords change this normal order. break leaves the loop right now. Use it when you have found what you were looking for. continue skips the rest of this iteration and jumps to the update.
// break: leave the loop immediatelyfor (let i = 1; i <= 10; i++) { if (i === 4) break; console.log(i); // 1 2 3, then the loop stops at 4}// continue: skip the rest of THIS iteration, go to the updatefor (let i = 1; i <= 5; i++) { if (i % 2 === 0) continue; console.log(i); // 1 3 5 — even numbers are skipped}/* Output:123135*/Off-by-one and other loop bugs
| Bug | Looks like | What happens |
|---|---|---|
| Off-by-one | i < 5 when you meant i <= 5 | The loop runs one time too few (or one time too many). This is the most common loop bug. Always check the first and the last value of i. |
| Accumulator inside the loop | for (…) { let sum = 0; sum += i; } | sum is reset in every iteration, so the answer is just the last i. |
| Update goes the wrong way | for (let i = 1; i <= 5; i--) | i never gets above 5, so the check is always true. This is an infinite loop (a loop that never stops). Press Ctrl+C to stop the program. |
| Wrong starting value | Product starting at 0 | Anything times 0 is 0. A product must start at 1. |
A recipe for writing any loop
- What values should i take? Write the first and the last one. They give you the start and the check.
- How does i move? By +1, −1, +2 and so on. This is the update.
- What happens in each iteration? This is the body.
- Do I need one answer at the end? If yes, add an accumulator before the loop.
- Dry-run the first two iterations and the last one. Most bugs show up there.
Practice questions
Use the recipe for each question. Write the dry-run table for at least the first three before you open the answers.
Print every number from 1 to N, one per line.
1 2 3 4 5
Hint 1
i start at, and which value is the last one printed?Hint 2
i <= N, and add 1 each time.Show all 3 solutions with explanations
First value 1, last value N, step +1, and the body prints i.
const N = 5;for (let i = 1; i <= N; i++) { console.log(i);}/* Output:12345*/The loop runs N times: i takes the values 1, 2, …, N, and the check fails when i becomes N + 1.
A while loop repeats as long as its condition is true. It has the same three parts as for, but spread out: start before the loop, check in the brackets, update at the end of the body.
const N = 5;let i = 1;while (i <= N) { console.log(i); i++;}/* Output:12345*/Forgetting i++ is the most common mistake here. Then i stays 1 forever, and the loop never ends. Lesson 5 covers while in detail.
Collect all the numbers into one string (a piece of text) and print it once at the end.
const N = 5;let out = "";for (let i = 1; i <= N; i++) { out += i + (i < N ? " " : ""); // a space after every number except the last}console.log(out); // 1 2 3 4 5This is useful when a problem asks for the numbers on one line. The i < N ? " " : "" part (a ternary, from Lesson 3) avoids a space after the last number.
Use the for loop (Approach 1). You know the exact range, and that is what for is made for.
Print the numbers from N down to 1.
4 3 2 1
Hint 1
Hint 2
i >= 1, and subtract 1 each time with i--.Show all 2 solutions with explanations
Start at N, check i >= 1, update with i--.
const N = 4;for (let i = N; i >= 1; i--) { console.log(i);}/* Output:4321*/A common mistake is keeping i <= N as the check. As i gets smaller, that stays true forever, so the loop never stops (an infinite loop).
Keep the normal loop from 1 to N, but print a value that you work out from i.
const N = 4;for (let i = 1; i <= N; i++) { console.log(N - i + 1);}/* Output:4321*/When i is 1, the value is N. When i is N, the value is 1. Printing a formula that uses the loop variable is a technique you will use often in pattern problems (Lesson 6).
Approach 1 is clearer. Approach 2 is worth knowing. It shows that the value you print does not have to be the loop variable itself.
Print all even numbers from 1 to N.
2 4 6 8 10
2 4 6
Hint 1
Hint 2
Show all 3 solutions with explanations
Loop from 1 to N and print i only when i % 2 === 0.
const N = 7;for (let i = 1; i <= N; i++) { if (i % 2 === 0) console.log(i);}/* Output:246*/This is a loop combined with an if from Lesson 3. The loop runs N times, but only about half of the numbers are printed.
Start at 2 and add 2 each time, so i is always even.
const N = 7;for (let i = 2; i <= N; i += 2) { console.log(i);}/* Output:246*/The loop runs only about N / 2 times and needs no if. Changing the update step is often the simplest way to skip values.
The k-th even number is 2 × k. Loop k from 1 while 2k ≤ N.
const N = 7;for (let k = 1; 2 * k <= N; k++) { console.log(2 * k);}/* Output:246*/This does the same amount of work as Approach 2, but it is written as a formula. Thinking “the k-th item is …” helps in many problems about sequences (lists of numbers that follow a rule).
Approach 2 is the best. It does the least work, and its purpose is clear. Saying that it halves the number of iterations shows that you think about efficiency.
Find 1 + 2 + … + N.
Hint 1
Hint 2
i on every iteration.Show all 2 solutions with explanations
- Before the loop:
sum = 0. - Inside the loop:
sum += i. - After the loop:
sumis the answer.
function sumTo(N) { let sum = 0; for (let i = 1; i <= N; i++) { sum += i; } return sum;}console.log(sumTo(5)); // 15console.log(sumTo(100)); // 5050The loop is wrapped in a function (a named block of code that you can run many times with different inputs; Lesson 7 teaches it) so we can test it with two inputs. For now, read sumTo(5) as “run this with N = 5”. The word return sends the answer back to the caller.
| i | sum after |
|---|---|
| 1 | 1 |
| 2 | 3 |
| 3 | 6 |
| 4 | 10 |
| 5 | 15 |
Pair the first and last numbers: 1 + 100, 2 + 99, and so on. Each pair adds up to N + 1, and there are N / 2 pairs.
function sumTo(N) { return (N * (N + 1)) / 2;}console.log(sumTo(5)); // 15console.log(sumTo(100)); // 5050There is no loop at all. The answer takes the same time for N = 5 or N = 5,000,000. Lesson 12 calls this O(1), which means constant time.
Know both. Write the loop if the question is about practising loops, and mention the formula afterwards. Interviewers like candidates who notice when a loop is not needed.
Find n! = n × (n − 1) × … × 1, for a whole number n ≥ 0.
Hint 1
Hint 2
Show all 3 solutions with explanations
Start with product = 1 and multiply by 2, 3, …, n.
function factorial(n) { let product = 1; // not 0 — anything × 0 is 0 for (let i = 2; i <= n; i++) { product *= i; } return product;}console.log(factorial(5)); // 120console.log(factorial(0)); // 1| i | product before | product after |
|---|---|---|
| 2 | 1 | 2 |
| 3 | 2 | 6 |
| 4 | 6 | 24 |
| 5 | 24 | 120 |
For n = 0 or 1 the loop never runs, so the answer stays 1. This is correct without a special case.
Multiply n × (n − 1) × … × 2, in the same order as the definition.
function factorial(n) { let product = 1; for (let i = n; i >= 2; i--) { product *= i; } return product;}console.log(factorial(5)); // 120Multiplication gives the same result in any order, so counting down works too.
Multiply by n, then reduce n by 1, until n reaches 1.
function factorial(n) { let product = 1; while (n > 1) { product *= n; n--; } return product;}console.log(factorial(5)); // 120The parameter n (the name for the input of a function) acts as the counter itself. Changing a number parameter inside a function does not change the value in the code that called it.
Any of the three is correct, and Approach 1 is the most common. Factorials grow very fast. From 23! onwards, a JavaScript number cannot store the exact value. So interview questions often ask for the answer modulo a number (the remainder after dividing by it; Lesson 13).
Print the multiplication table of n, from × 1 to × 10.
7 x 1 = 7 7 x 2 = 14 ... 7 x 10 = 70
Hint 1
Hint 2
i from 1 to 10 and print n, i and n * i.Show all 2 solutions with explanations
Loop i from 1 to 10 and build each line from pieces.
const n = 7;for (let i = 1; i <= 10; i++) { console.log(n + " x " + i + " = " + n * i);}/* Output:7 x 1 = 77 x 2 = 147 x 3 = 217 x 4 = 287 x 5 = 357 x 6 = 427 x 7 = 497 x 8 = 567 x 9 = 637 x 10 = 70*/n * i is worked out before the joining, because * has a higher priority than +.
Write the line as text between backticks, with ${ } placeholders for the values.
const n = 7;for (let i = 1; i <= 3; i++) { console.log(`${n} x ${i} = ${n * i}`);}/* Output:7 x 1 = 77 x 2 = 147 x 3 = 21*/This is easier to read than many + signs. There is also no risk of joining when you meant to add. (The example stops at 3 to keep the output short.)
Both print the same text. Template literals (Approach 2) are the newer way and are easier to read.
How many numbers from 1 to N are divisible by 3 or by 5?
Hint 1
Hint 2
i % 3 === 0 || i % 5 === 0.Show all 2 solutions with explanations
Visit every number from 1 to N. Count the ones that pass the test.
function countMultiples(N) { let count = 0; for (let i = 1; i <= N; i++) { if (i % 3 === 0 || i % 5 === 0) count++; } return count;}console.log(countMultiples(15)); // 7This is a counting accumulator. The || (OR) makes one yes or no decision per number, so a number that is divisible by both is counted once.
- Multiples of 3 up to N:
Math.floor(N / 3). - Multiples of 5:
Math.floor(N / 5). - Numbers divisible by both (multiples of 15) were counted twice, so subtract
Math.floor(N / 15)once.
function countMultiples(N) { return Math.floor(N / 3) + Math.floor(N / 5) - Math.floor(N / 15);}console.log(countMultiples(15)); // 7console.log(countMultiples(100)); // 47For N = 15: 5 multiples of 3, plus 3 multiples of 5, minus 1 multiple of 15, gives 7. No loop is needed, so it is instant for any N.
Start with the loop (Approach 1), because it is easy to get right. If the interviewer says N can be a billion, the formula (Approach 2) is the answer they want.
For every number from 1 to N, print Fizz (divisible by 3), Buzz (divisible by 5), FizzBuzz (divisible by both) or the number itself. This is LeetCode 412.
1 2 Fizz 4 Buzz … 14 FizzBuzz
Hint 1
Hint 2
Show all 2 solutions with explanations
The answer from Lesson 3 for a single number, now run for every number from 1 to N.
const N = 15;for (let i = 1; i <= N; i++) { if (i % 15 === 0) console.log("FizzBuzz"); else if (i % 3 === 0) console.log("Fizz"); else if (i % 5 === 0) console.log("Buzz"); else console.log(i);}/* Output:12Fizz4BuzzFizz78FizzBuzz11Fizz1314FizzBuzz*/Divisible by both 3 and 5 is the same as divisible by 15, so the first check can be shorter.
Keep two counters that count up to 3 and 5 and then go back to 0. No division is needed at all.
const N = 6;let three = 0, five = 0;for (let i = 1; i <= N; i++) { three++; five++; let word = ""; if (three === 3) { word += "Fizz"; three = 0; } if (five === 5) { word += "Buzz"; five = 0; } console.log(word || i);}/* Output:12Fizz4BuzzFizz*/word || i gives the word if it is not empty, and otherwise gives the number. (An empty string is falsy, from Lesson 3.) Interviewers sometimes ask this as a follow-up: “solve it without using %”.
Approach 1 is the standard answer. Approach 2 is useful if the interviewer adds the rule “no % allowed”.
Each Fibonacci number is the sum of the two numbers before it, starting with 0 and 1. Print the first N numbers on one line.
Hint 1
a and b.Hint 2
a, compute next = a + b, then move both forward: a = b, b = next.Show all 2 solutions with explanations
a is the current number and b is the next one. After printing a, move both one place forward.
function fibonacci(N) { let a = 0, b = 1; let result = ""; for (let i = 1; i <= N; i++) { result += a + " "; const next = a + b; a = b; b = next; } return result.trim();}console.log(fibonacci(7)); // 0 1 1 2 3 5 8console.log(fibonacci(1)); // 0| i | print a | next = a + b | a, b after |
|---|---|---|---|
| 1 | 0 | 1 | 1, 1 |
| 2 | 1 | 2 | 1, 2 |
| 3 | 1 | 3 | 2, 3 |
| 4 | 2 | 5 | 3, 5 |
| 5 | 3 | 8 | 5, 8 |
| 6 | 5 | 13 | 8, 13 |
| 7 | 8 | 21 | 13, 21 |
Store every number in an array (a list). Build each new number from the last two entries.
function fibonacci(N) { const fib = [0, 1]; for (let i = 2; i < N; i++) { fib.push(fib[i - 1] + fib[i - 2]); } return fib.slice(0, N).join(" ");}console.log(fibonacci(7)); // 0 1 1 2 3 5 8console.log(fibonacci(1)); // 0Arrays are covered in Lesson 8. push adds an item to the end. fib[i - 1] reads the item at a position. slice(0, N) keeps the first N items. join(" ") turns the list into one string with spaces. This uses more memory, but you can look up any earlier number.
Approach 1 uses only two variables, however big N is, so give it in an interview. Approach 2 is useful when you need to look back at earlier values. This is the idea behind dynamic programming (Lesson 54), a method that saves the answers to small problems and reuses them.
A prime is a whole number greater than 1 that is divisible only by 1 and itself. Return whether n is prime.
Hint 1
Hint 2
break.Hint 3
Show all 3 solutions with explanations
Assume n is prime. Try each divisor. If one divides n exactly, change the answer to “not prime” and stop.
function isPrime(n) { if (n < 2) return false; let prime = true; // a "flag": yes until proven no for (let d = 2; d < n; d++) { if (n % d === 0) { prime = false; break; // one divisor is enough } } return prime;}console.log(isPrime(7)); // trueconsole.log(isPrime(12)); // falseconsole.log(isPrime(1)); // falseThe flag variable prime (a true/false variable that records a yes or no answer) starts as true. It is switched to false as soon as a divisor is found. For a prime like 97, this checks 95 divisors.
If n = a × b, one of a and b is at most √n. So if no number up to √n divides n, no larger number can divide it either.
function isPrime(n) { if (n < 2) return false; for (let d = 2; d * d <= n; d++) { if (n % d === 0) return false; } return true;}console.log(isPrime(97)); // trueconsole.log(isPrime(91)); // falsed * d <= n means “d is at most √n”, and it needs no square root calculation. For 97 that is only 8 divisors (2 to 9) instead of 95. For 91, the loop finds 7 (91 = 7 × 13).
Handle 2 on its own. After that, no even number can divide an odd n, so try only odd divisors.
function isPrime(n) { if (n < 2) return false; if (n % 2 === 0) return n === 2; // 2 is the only even prime for (let d = 3; d * d <= n; d += 2) { if (n % d === 0) return false; } return true;}console.log(isPrime(2)); // trueconsole.log(isPrime(97)); // trueconsole.log(isPrime(100)); // falseThis halves the work again. The update d += 2 visits 3, 5, 7, 9, and so on.
Give Approach 2 in an interview. It is short and much faster than Approach 1. Mention Approach 3 as a further improvement. Lesson 13 shows how to find all primes up to N at once with the Sieve of Eratosthenes.
Make it stick
- Write the order of execution from memory: start, check, body, update, and so on. Where does the loop end?
- Without running it: how many times does
for (let i = 3; i <= 9; i++)run? Andfor (let i = 0; i < 9; i += 3)? (7 and 3.) - Write factorial from scratch and dry-run it for n = 4 in a table.
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: the while loop, which you use when you do not know in advance how many times to repeat. You will also learn to take a number apart digit by digit.
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