Skip to content
academia.sh

Lesson 12 / 21

Loops

Counted, conditional, and collection loops; the `for...in` versus `for...of` distinction, loop control, labeled exit, and traversing objects.

Contents

The previous lesson classified a single measurement. Processing the entire list requires repetition. The Programming Fundamentals course’s Loops and Loop Control lessons established the purpose of these structures and the loop-invariant concept; here, the forms JavaScript offers and the difference between them — especially between the two collection loops — are covered.

There are four loop forms, and the choice among them is not arbitrary: the counted loop is used when an index is needed, the conditional loop when the repeat count is not known in advance, and the collection loops when working directly on elements.

Counted and Conditional Loops

The classic for loop has three parts: initialization, condition, and end-of-round action. As shown in the Variable Declarations lesson, the counter is declared with let and rebound on every round.

const temperatures = [21.5, 19.75, 23, 18.25];

for (let i = 0; i < temperatures.length; i++) {
  console.log(i, temperatures[i]);
}
0 21.5
1 19.75
2 23
3 18.25

The while loop carries only a condition; it is used when the repeat count is not known in advance:

const temperatures = [21.5, 19.75, 23, 18.25];
let i = 0;
while (i < temperatures.length && temperatures[i] < 23) {
  i += 1;
}
console.log("first index at or above 23:", i);
first index at or above 23: 2

The order in the condition matters. The bound check (i < temperatures.length) is written first; thanks to short-circuit evaluation, the second comparison never runs once the index is exceeded. Written in reverse order, the comparison undefined < 23 would run at the end of the array — this expression gives false because it goes through a coercion that produces NaN, so the loop would still end, but the reason would be coincidence rather than the guard.

The do...while form runs the body at least once; the condition is tested at the end of the round:

let round = 0;
do {
  round += 1;
} while (round < 3);
console.log("round:", round);
round: 3

Two Collection Loops

JavaScript has two collection loops that resemble each other and give different things.

for...in gives an object’s enumerable property keys. for...of gives an iterable value’s elements.

const temperatures = [21.5, 19.75, 23];

for (const index in temperatures) {
  console.log(typeof index, index);
}
for (const value of temperatures) {
  console.log(typeof value, value);
}
string 0
string 1
string 2
number 21.5
number 19.75
number 23

The first loop gave indices, and the indices are of string type. The reason is that object keys are strings; an array is also an object and carries its indices as keys. This explains why code that sums with for...in ends up concatenating text — the addition rule from the Type Conversion and Coercion lesson kicks in.

The difference becomes even clearer once a property is added to the array:

const temperatures = [21.5, 19.75, 23];
temperatures.source = "A1";

for (const index in temperatures) {
  console.log("for...in:", index);
}
for (const value of temperatures) {
  console.log("for...of:", value);
}
console.log("length:", temperatures.length);
for...in: 0
for...in: 1
for...in: 2
for...in: source
for...of: 21.5
for...of: 19.75
for...of: 23
length: 3

for...in also traversed a key that is not an array element; for...of gave only the elements. The length value did not change either — the added property does not count toward the array’s length.

This gives the course’s rule: for...of is used on arrays. for...in is written only to traverse plain objects’ keys.

For the case where both index and value are wanted together, there is a separate method:

const temperatures = [21.5, 19.75, 23];
for (const [index, value] of temperatures.entries()) {
  console.log(index, value);
}
0 21.5
1 19.75
2 23

Here the index is of number type. The bracketed spelling splits the pair’s two components into two names; this destructuring form is covered in detail in the Arrays and Objects lessons.

Traversing Objects

Plain objects cannot be traversed with for...of:

const record = { station: "A1", temperature: 21.5, humidity: 48 };
try {
  for (const d of record) {
    console.log(d);
  }
} catch (error) {
  console.log(error.name + ": " + error.message);
}
TypeError: record is not iterable

The message is exact: the object is not iterable. Arrays, strings, and some built-in collections are iterable; plain objects are not.

There are two paths:

const record = { station: "A1", temperature: 21.5, humidity: 48 };

for (const key in record) {
  console.log(key, record[key]);
}
for (const [key, value] of Object.entries(record)) {
  console.log(key, "=", value);
}
station A1
temperature 21.5
humidity 48
station = A1
temperature = 21.5
humidity = 48

The two gave the same result, but their scope is not the same. for...in also traverses inherited properties; Object.entries gives only the object’s own properties. The inheritance chain is a later course’s subject; for now, it is enough to know the second form is narrower and more predictable. In this course, object traversal will be written with Object.entries.

Loop Control

break ends a loop, continue skips that round. Both affect the innermost loop.

const records = [
  { station: "A1", temperature: 21.5 },
  { station: "A2", temperature: NaN },
  { station: "B1", temperature: 23 },
  { station: "B2", temperature: 18.25 },
];

let total = 0;
let count = 0;
for (const record of records) {
  if (Number.isNaN(record.temperature)) {
    continue;
  }
  total += record.temperature;
  count += 1;
}
console.log("valid readings:", count, "average:", total / count);

let firstHigh = null;
for (const record of records) {
  if (record.temperature > 22) {
    firstHigh = record.station;
    break;
  }
}
console.log("first high:", firstHigh);
valid readings: 3 average: 20.916666666666668
first high: B1

The first loop skipped the invalid record and computed the average only over valid readings — this is why count had to be used as the divisor instead of records.length. The second loop stopped once it found what it was looking for; the remaining records were never checked.

The average’s digits are the result of the floating-point representation covered in the Primitive Types lesson: the result of 62.75 / 3 cannot be written exactly as a binary fraction and is rounded to the nearest representable value.

Breaking out of an outer loop from a nested one requires a label:

const grid = [[1, 2], [3, 4], [5, 6]];
outerLoop: for (const row of grid) {
  for (const cell of row) {
    if (cell === 4) {
      console.log("found, breaking both loops");
      break outerLoop;
    }
    console.log("looking at:", cell);
  }
}
looking at: 1
looking at: 2
looking at: 3
found, breaking both loops

A label is a name placed in front of a loop; break and continue take this name to say which loop they affect. An unlabeled break would have broken only the inner loop, and the outer loop would have moved on to the next row.

Labeled exit is rarely needed. Wrapping the same work in a function and exiting with return is usually more readable; function definition is the next lesson’s subject.

Summary

  • Counted for is used when an index is needed, while when the repeat count is unknown, do...while when the body must run at least once.
  • for...in gives property keys, and the keys are of string type; for...of gives elements.
  • for...of is used on arrays; for...in also traverses non-array properties.
  • Plain objects are not iterable; key–value traversal is written with Object.entries.
  • break and continue affect the innermost loop; a label is used to reach an outer one.

Next Step

In this lesson, checks and accumulators were embedded in the loop body; doing the same work a second time would mean copying code. The next lesson moves to functions and opens with a distinction specific to JavaScript: the difference between writing a function as a declaration and as an expression is not just syntax — it also determines when the name becomes accessible.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close