RizTech Academy logo
RizTech Academy
Language BasicsLesson 5 of 520 min

Loops and iteration

Computers are good at doing the same thing many times without getting bored. JavaScript has five ways to ask for that, and one of them hands you string indices when you were expecting numbers.

for

The classic counted loop:

for (let i = 1; i <= 3; i++) {
  console.log(`Day ${i}`);
}
Day 1
Day 2
Day 3

Three parts separated by semicolons: start, keep going while, and after each pass. Use let, not var — module 2's first lesson showed why.

Off-by-one errors live here. i <= 3 runs three times starting from 1; i < 3 starting from 0 also runs three times, giving 0, 1 and 2. Since arrays are numbered from zero, the second form is the one you will write most.

while

When you do not know in advance how many passes you need:

let balance = 500;
let days = 0;

while (balance >= 80) {
  balance -= 80;
  days++;
}

console.log(`${days} days of tiffin, ₹${balance} left over.`);
6 days of tiffin, ₹20 left over.

Something inside the loop must eventually make the condition false. Forget that and you have an infinite loop: in Node, Ctrl + C; in a browser tab, the page freezes and you close the tab. It will happen to you at least once.

do...while is the rare variant that always runs at least once, because it checks at the bottom:

let n = 0;
let runs = 0;
do {
  runs++;
} while (n > 0);

console.log(runs);
1

The condition was false from the start and the body still ran once. Use it when you must ask before you can test the answer.

for...of — the one to reach for

For going through the values of an array or string:

const orders = ['Priya', 'Arjun', 'Meera'];

for (const customer of orders) {
  console.log(`Tiffin for ${customer}`);
}
Tiffin for Priya
Tiffin for Arjun
Tiffin for Meera

No counter, no off-by-one, no orders[i]. This is the default loop for going through a collection. const is correct here — each pass gets its own fresh binding, so nothing is being reassigned.

It works on strings too:

for (const character of 'dal') {
  console.log(character);
}
d
a
l

When you need the position as well, use .entries():

for (const [index, customer] of orders.entries()) {
  console.log(`${index + 1}. ${customer}`);
}
1. Priya
2. Arjun
3. Meera

The square brackets are destructuring, which is module 4. Take it on trust for now.

The trap: for...in is not for arrays

for...in looks like the same thing with a different preposition. It is not.

const orders = ['Priya', 'Arjun'];

for (const item of orders) {
  console.log(item);
}

for (const item in orders) {
  console.log(item);
}
Priya
Arjun
0
1

for...in gives you the keys, not the values. And those keys are strings, not numbers — which is where it stops being a nuisance and becomes a bug:

for (const i in orders) {
  console.log(i + 1);
}
01
11

You expected 1 and 2. You got '01' and '11', because i is the string '0' and + joins text. Silent, plausible and wrong — exactly the coercion bug from the types lesson, delivered by a loop.

for...in has a second problem: it also walks properties inherited from elsewhere, so a library that adds something to every array can put extra iterations into your loop.

Use for...of for arrays. Use for...in only for the keys of a plain object — which module 4 covers, alongside better tools for that too.

One more thing for...of will not do: plain objects.

for (const x of { plates: 3 }) {
  console.log(x);
}
TypeError: {(intermediate value)} is not iterable

Objects are not iterable. Object.keys(), Object.values() and Object.entries() are the answer, in module 4.

Stopping early: break and continue

break leaves the loop; continue skips to the next pass.

const plates = [3, 5, 0, 4, 2];

for (const count of plates) {
  if (count === 0) continue;
  console.log(`${count} plates`);
}
3 plates
5 plates
4 plates
2 plates
let running = 0;

for (const count of plates) {
  running += count;
  if (running > 8) {
    console.log(`Stopped at ${running}.`);
    break;
  }
}
Stopped at 12.

Both only affect the innermost loop they are inside. Breaking out of two nested loops needs a flag or — better — moving the inner loop into a function and returning from it. Functions are the next module.

Which loop to use

Loop Use it for
for...of Default. Values of an array, string or Map.
for (let i = ...) When you need the index, or a step other than 1, or to count backwards.
while When the number of passes is not known up front.
do...while When the body must run at least once.
for...in Keys of a plain object. Never an array.

Module 4 adds map, filter, reduce and forEach, which replace most of the loops you would otherwise write for arrays. They are not a different kind of loop so much as a way of saying what the loop is for. Learn these first — every one of those methods is a loop underneath, and debugging them is much easier when you can picture it.

Check your work

for (let i = 1; i <= 3; i++) runs three times, printing Day 1, 2 and 3. With i = 0; i < 3 it also runs three times, printing 0, 1, 2 — the form you want for array indices.

The ₹500 balance loop gives 6 days of tiffin, ₹20 left over. Six lots of ₹80 is ₹480, and ₹20 is not enough for a seventh.

do...while with a false condition still runs once, printing 1. It tests at the bottom.

for...in over ['Priya', 'Arjun'] prints 0 and 1, not the names — those are the keys. for...of prints the names.

i + 1 inside a for...in over an array gives '01' and '11'. The keys are strings, and + joins text rather than adding. Use for...of, or .entries() when you need a real numeric index.

for...of over a plain object throws TypeError: {(intermediate value)} is not iterable. Objects are not iterable; use Object.entries() in module 4.

continue on count === 0 skips the zero and prints the other four counts. break once the running total passes 8 stops at 12, because 3 + 5 + 0 + 4 reaches 12 on the fourth pass — the check happens after adding, so it reports the value that broke the limit rather than the one before it.

break and continue affect only the innermost loop.

₹1000 at ₹80 a day is 12 days with ₹40 left over.

The digits of '411014' add up to 11. If you forget Number() on each character you get the string '0411014' instead — a running total that started as 0 and had six characters joined onto it.

The running-total exercise, with [3, 5, 0, 4, 2] at ₹80:

const plates = [3, 5, 0, 4, 2];
let total = 0;
let delivered = 0;

for (const [i, count] of plates.entries()) {
  if (count === 0) continue;
  total += count * 80;
  delivered++;
  console.log(`Day ${i + 1}: ${count} plates, running ₹${total}`);
}

console.log(`Total ₹${total} over ${delivered} days`);
Day 1: 3 plates, running ₹240
Day 2: 5 plates, running ₹640
Day 4: 4 plates, running ₹960
Day 5: 2 plates, running ₹1120
Total ₹1120 over 4 days

Day 3 is missing from the output, which is the continue doing its job — but note the day numbers stay correct, because they come from .entries() rather than from a counter you increment yourself. A separate counter would have renumbered day 4 as day 3.

Practice

  1. Print the numbers 1 to 10 with a for loop. Then print them backwards. Then print only the even ones, two different ways — with % and with a step of 2.
  2. Print Day 1 to Day 7 and predict the loop bounds before running it.
  3. Use a while loop to work out how many days ₹1000 lasts at ₹80 a day, and how much is left over.
  4. Write an infinite loop on purpose in Node and stop it with Ctrl + C. Knowing what it looks like — and that you are not stuck — is worth the thirty seconds.
  5. Loop over ['Priya', 'Arjun', 'Meera'] with for...of and print a line for each.
  6. Do the for...in experiment. Loop over the same array with for...in, print each key, then print key + 1 and explain the '01' to yourself out loud before fixing it.
  7. Use .entries() to print a numbered list starting at 1.
  8. Loop over '411014' character by character and add the digits up. You will need Number() on each character — and if you forget, you will get '0411014', which is the coercion bug once more.
  9. Use continue to skip cancelled orders (a count of 0) and break to stop once a running total passes 10.
  10. Harder. Given [3, 5, 0, 4, 2] as plates per day at ₹80, print a line per day with a running total, skip the cancelled days, and print the final total and the number of days actually delivered. Do it with one loop, not three.

That is module two. You can now hold values, know what those values actually are, build text out of them, make decisions, and repeat work — and you have met the three bugs that come from JavaScript's willingness to convert things behind your back: '10' > '9', a truthy '0', and a falsy real zero.

Those are the bugs you will actually write. The next module is where the language stops being a list of features and starts having a shape: functions, scope, closures, and this. It is the conceptual core of JavaScript, and it is the part that most people never quite nail down.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship