WasiLearn Code
JavaScript · Making decisions and repeating

Loops: doing it again

You have eleven players and you want to print each name. You could write eleven console.log lines, and then the squad changes and you rewrite them all. A loop says the instruction once and applies it to every item.

let players = ["Babar", "Rizwan", "Shaheen"];

for (const player of players) {
  console.log(player);
}
output
Babar
Rizwan
Shaheen

Three names, one instruction, and it works just as well with three hundred.

for...of is the one to reach for

for...of walks through the items of an array and hands you each one in turn. There is no counting and no index arithmetic, which removes the whole class of bug in the next section.

let prices = [250, 120, 80];
let total = 0;

for (const price of prices) {
  total = total + price;
}

console.log(total);
output
450

const inside the loop is correct even though the value changes each time round. Each pass creates a fresh player or price; nothing is reassigned.

It works on strings too, since a string is a sequence of characters:

for (const letter of "chai") {
  console.log(letter);
}
output
c
h
a
i

The classic for loop

When you need the position — numbering a list, or comparing an item to the one before it — the counting loop earns its keep.

let players = ["Babar", "Rizwan", "Shaheen"];

for (let i = 0; i < players.length; i++) {
  console.log(`${i + 1}. ${players[i]}`);
}
output
1. Babar
2. Rizwan
3. Shaheen

The three parts inside the brackets, separated by semicolons:

  • let i = 0 — where to start. Arrays begin at 0, so counters do too.
  • i < players.length — keep going while this is true.
  • i++ — what to do after each pass.

i is the conventional name and it stands for index. Use let, not const, because it genuinely is reassigned each time.

The off-by-one error

The condition is where this loop goes wrong, and it goes wrong in exactly two ways.

let players = ["Babar", "Rizwan", "Shaheen"];

for (let i = 0; i < players.length - 1; i++) {
  console.log(players[i]);
}
output
Babar
Rizwan

Shaheen never gets printed. The array has 3 items at positions 0, 1 and 2; length - 1 is 2, and i < 2 stops at 1. The - 1 looks like it is compensating for zero-based counting, and it is compensating for something that < already handled.

The opposite mistake runs one too far:

let players = ["Babar", "Rizwan", "Shaheen"];

for (let i = 0; i <= players.length; i++) {
  console.log(players[i]);
}
output
Babar
Rizwan
Shaheen
undefined

i <= 3 includes position 3, which does not exist, so JavaScript hands back undefined rather than raising an error. In a real program that undefined flows onward and breaks something far away.

The correct form is i = 0; i < length; i++, with a strict less-than and no arithmetic. Write it as one unit and do not improvise. Better still, use for...of when you do not need i, and the question never comes up.

while, for when you cannot count in advance

while repeats as long as a condition holds. It suits situations where the number of passes is not known when the loop starts.

let seatsLeft = 3;

while (seatsLeft > 0) {
  console.log(`Seat sold, ${seatsLeft - 1} left`);
  seatsLeft = seatsLeft - 1;
}
output
Seat sold, 2 left
Seat sold, 1 left
Seat sold, 0 left

The danger is obvious once stated: something inside the loop must eventually make the condition false. Forget that line and the loop runs forever, the page freezes, and the tab has to be closed. Before writing the body, write the line that changes the condition.

break and continue

break leaves the loop immediately. continue skips the rest of this pass and starts the next one.

let marks = [72, 45, 88, 30, 91];

for (const mark of marks) {
  if (mark < 50) {
    continue;
  }
  console.log(mark);
}
output
72
88
91
let marks = [72, 45, 88, 30, 91];

for (const mark of marks) {
  if (mark > 85) {
    console.log(`First distinction: ${mark}`);
    break;
  }
}
output
First distinction: 88

break is how you stop once you have found what you were looking for. Without it, the loop keeps checking every remaining item for no reason — and would print 91 as well.

Loops inside loops

The inner loop runs completely for every single pass of the outer one.

let sizes = ["small", "large"];
let drinks = ["chai", "coffee"];

for (const size of sizes) {
  for (const drink of drinks) {
    console.log(`${size} ${drink}`);
  }
}
output
small chai
small coffee
large chai
large coffee

Two lists of two give four lines. That multiplication is the thing to watch: two lists of a thousand give a million passes, and a page that stops responding. Nesting is fine for small, known lists and worth rethinking for large ones.

Looping over an object

for...of does not work on plain objects, because an object is not a sequence. Object.entries turns it into one:

let stock = { chai: 12, coffee: 4, lassi: 0 };

for (const [item, count] of Object.entries(stock)) {
  console.log(`${item}: ${count}`);
}
output
chai: 12
coffee: 4
lassi: 0

Object.keys and Object.values give just one side each. There is also for...in, which loops over keys — it behaves surprisingly with inherited properties, and Object.keys is the safer habit.

The array methods that replace loops

Much of what loops are used for has a shorter form. forEach runs a function for each item; map builds a new array from the results; filter keeps the items that pass a test.

let prices = [250, 120, 80];

let withTax = prices.map(price => Math.round(price * 1.15));
let cheap = prices.filter(price => price < 200);

console.log(withTax);
console.log(cheap);
console.log(prices.reduce((sum, price) => sum + price, 0));
output
[ 288, 138, 92 ]
[ 120, 80 ]
450

Each of those is a loop underneath, with the counting hidden and the intent visible in the name. They get a lesson of their own; for now it is enough to know that when you find yourself writing a for loop that builds a new array, map probably says it in one line.

Which loop to use

  • Every item, no index needed → for...of. This is most of the time.
  • You need the position → for (let i = 0; i < n; i++).
  • Unknown number of passes → while, with the exit line written first.
  • Building a new array, or a total → map, filter, reduce.

And whenever you reach for the counting loop, write the condition as i < length. Strictly less than, no arithmetic. That single habit removes the most common loop bug there is.

Prefer learning with a teacher?

WasiLearn Academy runs small-group classes covering this material, online and in Karachi.

See the classes