Frontend Master

JavaScript Curriculum

Do It Again (and Again)
+40 XP

Do It Again (and Again)

medium
~22 min·40 XP

Nexus has 2,400 user accounts. Your task: find every account that hasn't logged in for 90 days and mark it inactive. You could write 2,400 if statements. Or you could write one loop that does the same check for every single account — and handles 240,000 just as easily.

The problem with doing things once

A function runs when you call it. That's one execution. But most real problems require the same logic applied to every item in a collection — every user, every product, every row in a table.

Loops are the mechanism that repeats a block of code as many times as needed, controlled by a condition you define. They're one of the most fundamental building blocks in all of programming.


The for loop — the classic, explicit choice

The for loop packs everything into one line: where to start, when to stop, how to advance.

js
for (let i = 0; i < 5; i++) { console.log(i) } // 0 // 1 // 2 // 3 // 4

Three parts, separated by semicolons inside the parentheses:

PartExampleJob
initlet i = 0Runs once before the first iteration — sets up the counter
conditioni < 5Checked before every iteration — if false, the loop stops
updatei++Runs after every iteration body — advances the counter

Step through the loop below. Watch each part light up in sequence — and notice exactly when the condition check causes the loop to end:

for loop tracer — watch each part activate
① init② condition③ update
for (let i = 0;i < 5;i++) {
console.log(scores[i])
}
i =
condition

output

nothing logged yet…

🧠The loop lifecycle
init → [check → body → update] → [check → body → update] → … → check fails → done. The condition is checked BEFORE each iteration, including the first. If it's false from the start, the body never runs at all.

Four loop types — same problem, different tools

JavaScript gives you four ways to loop. The right choice depends on what you're iterating over and what information you need inside the loop.

use when

You know exactly how many times to loop, or need the index.

for (let i = 0; i < products.length; i++) {
console.log(i, products[i].name)
}
// → 0 "Nexus"
// → 1 "Orbit"
// → 2 "Pulse"

anatomy

init ; condition ; update — all three in one place. Most explicit.

The practical decision tree:

  • Iterating an array and need the value? → for...of
  • Iterating an array and need the index? → for with counter
  • Iterating an object's keys? → for...in
  • Looping until a condition changes (not a fixed collection)? → while

The while loop — condition-driven repetition

while keeps looping as long as its condition is true. There's no built-in counter — you manage state yourself:

js
let attempts = 0 while (attempts < 3) { console.log(`Attempt ${attempts + 1}`) attempts++ // ← CRITICAL: must change something, or loop runs forever } // "Attempt 1" // "Attempt 2" // "Attempt 3"
⚠️The infinite loop trap
If your while condition never becomes false, your program hangs forever. Always make sure something inside the loop body changes the condition. The most common mistake: forgetting to increment the counter, or writing a condition that can never be false.

A classic while use-case — keep trying until it works:

js
let connected = false let retries = 0 while (!connected && retries < 5) { connected = tryConnect() // some function that might succeed retries++ }

This is impossible to express cleanly as a for loop because you don't know how many iterations you'll need.


for...of — the clean modern choice for arrays

When you want every value from an array and don't care about the index, for...of is the most readable option:

js
const products = ["Nexus", "Orbit", "Pulse"] for (const product of products) { console.log(product) } // "Nexus" // "Orbit" // "Pulse"

No counter, no products[i], no index arithmetic. The variable product is a new const binding for each iteration — safe, clear, and impossible to accidentally mutate.

for...of works on any iterable: arrays, strings, Set, Map, and more.

js
// Iterate a string character by character for (const char of "Nexus") { console.log(char) // "N", "e", "x", "u", "s" }

for...in — iterating object keys

for...in gives you the keys of an object, one by one:

js
const user = { name: "Alex", role: "admin", level: 4 } for (const key in user) { console.log(key, "→", user[key]) } // "name" → "Alex" // "role" → "admin" // "level" → 4

Note that user[key] uses bracket notation — key is a variable containing the property name, so dot notation won't work.

⚠️Never use for...in on arrays
for...in iterates enumerable properties — on arrays that's the indexes as strings ("0", "1", "2"), plus any other enumerable properties on the array prototype. Use for...of or a regular for loop for arrays. Reserve for...in strictly for plain objects.

break and continue — steering the loop

Two keywords give you fine-grained control over loop execution. Step through the demo to see the difference:

break vs continue — step through the loop
for (const product of products) {
if (product.score < 50) {
break // stop the entire loop
}
console.log(product.name)
}
Nexus
score: 88
Orbit
score: 42
Pulse
score: 95
Vega
score: 61
Flare
score: 30

console output

nothing logged yet…

js
const products = [ { name: "Nexus", score: 88 }, { name: "Orbit", score: 42 }, // below threshold { name: "Pulse", score: 95 }, ] // break — stop everything when first bad item found for (const product of products) { if (product.score < 50) break console.log(product.name) } // "Nexus" ← only one, then loop stops entirely // continue — skip bad item, keep going for (const product of products) { if (product.score < 50) continue console.log(product.name) } // "Nexus" // "Pulse" ← Orbit skipped, Pulse still runs

break = emergency exit — terminates the entire loop immediately. continue = skip and move on — jumps to the next iteration without running the rest of the body.


Building the inactive-user scanner

Here's the pattern your challenge asks you to build:

js
function findInactiveUsers(users) { const inactive = [] for (const user of users) { if (user.daysSinceLogin > 90) { inactive.push(user) } } return inactive }

for...of is the right choice here: you want every value, you don't need the index, and you're filtering by a condition on each item's properties.

✗ crashes
✓ works

Both work. The for...of version removes all the index plumbing that has nothing to do with the actual logic.

Challenge

Given an array of user objects, each with a name and daysSinceLogin, write a function called findInactiveUsers(users) that returns a new array containing only the users whose daysSinceLogin is greater than 90. Use a for...of loop — don't use .filter(). Log each inactive user's name and daysSinceLogin.

loopsforwhilefor-offor-inbreakcontinueiteration
Do It Again (and Again) | Nexus Learn