Skip to main content

πŸ” Lesson 3.2: Loops β€” for and while

Why type the same thing a hundred times when you can tell the computer to do it for you? Loops are how programs handle repetition.

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Write for loops in all three languages (counting-style and collection-style)
  • Understand Python's range() vs. the classic for (init; cond; step) pattern in JS/C#
  • Use while loops for condition-driven repetition
  • Control loop flow with break and continue
  • Recognize when to use for vs. while

Estimated Time: 45 minutes

Project: A multiplication table generator and a number-guessing game

πŸ“‘ In This Lesson

Why Loops?

Imagine you need to print the numbers 1 through 1000. Without loops, that's 1000 print statements. With a loop, it's three lines. Loops let your code repeat a block of instructions as many times as needed β€” whether that's a fixed count, once for each item in a list, or until some condition changes.

graph TD A["Start"] --> B{"More items
to process?"} B -->|Yes| C["Do the work"] C --> D["Move to next item"] D --> B B -->|No| E["Done!"] style B fill:#f59e0b,stroke:#d97706,color:#fff,stroke-width:2px style C fill:#10b981,stroke:#059669,color:#fff,stroke-width:2px

There are two main flavors of loops:

  • for loops β€” best when you know how many times to repeat (or are iterating over a collection)
  • while loops β€” best when you repeat until a condition becomes false

Counting for Loops

The most common loop: do something a specific number of times. This is where the three languages diverge most.

# Python: for + range()
# range(1, 6) produces 1, 2, 3, 4, 5  (start inclusive, stop exclusive)
for i in range(1, 6):
    print(i)

# range() with one argument starts at 0:
# range(5) β†’ 0, 1, 2, 3, 4

# range() with three arguments adds a step:
# range(0, 10, 2) β†’ 0, 2, 4, 6, 8  (count by 2s)
# range(10, 0, -1) β†’ 10, 9, 8, ... 1  (count backwards)
// JavaScript: classic three-part for loop
// for (initialize; condition; update)
for (let i = 1; i <= 5; i++) {
    console.log(i);
}

// Breaking it down:
// let i = 1    β†’ runs once before the loop starts
// i <= 5       β†’ checked before each iteration
// i++          β†’ runs after each iteration

// Count by 2s:
// for (let i = 0; i < 10; i += 2) { ... }

// Count backwards:
// for (let i = 10; i >= 1; i--) { ... }
// C#: same three-part syntax as JavaScript
for (int i = 1; i <= 5; i++)
{
    Console.WriteLine(i);
}

// The pattern is identical to JavaScript:
// for (initialize; condition; update)

// Count by 2s:
// for (int i = 0; i < 10; i += 2) { ... }

// Count backwards:
// for (int i = 10; i >= 1; i--) { ... }

Syntax Comparison

Task 🐍 Python ⚑ JavaScript / πŸ”· C#
0 to 4 for i in range(5) for (let i = 0; i < 5; i++)
1 to 10 for i in range(1, 11) for (let i = 1; i <= 10; i++)
Even numbers 0–8 for i in range(0, 10, 2) for (let i = 0; i < 10; i += 2)
Countdown 5 to 1 for i in range(5, 0, -1) for (let i = 5; i >= 1; i--)

⚠️ Python's range() Is Exclusive at the End

range(1, 5) gives you 1, 2, 3, 4 β€” not 5. The stop value is excluded. This is the most common beginner mistake with range(). If you want 1 through 5, use range(1, 6).

πŸŽ“ Instructor Note: Delivery Guidance

Students coming from math will expect "1 to 5" to include 5. Spend a moment explaining why Python's exclusive-end convention exists (it makes range(len(list)) work cleanly and avoids off-by-one errors in many cases). For the JS/C# side, emphasize reading the three-part for loop like a sentence: "start at 1, keep going while ≀ 5, add 1 each time." Have students trace through a loop with pen and paper for their first few examples.

Looping Over Collections

Often you don't want to count β€” you want to do something with each item in a list. All three languages have a convenient way to do this.

# Python: for...in (same keyword, no range needed)
fruits = ["apple", "banana", "cherry"]

for fruit in fruits:
    print(f"I like {fruit}!")

# Output:
# I like apple!
# I like banana!
# I like cherry!

# Need the index too? Use enumerate():
for index, fruit in enumerate(fruits):
    print(f"{index}: {fruit}")
# 0: apple
# 1: banana
# 2: cherry
// JavaScript: for...of (not for...in!)
let fruits = ["apple", "banana", "cherry"];

for (let fruit of fruits) {
    console.log(`I like ${fruit}!`);
}

// Need the index too? Use forEach() or entries():
fruits.forEach((fruit, index) => {
    console.log(`${index}: ${fruit}`);
});

// ⚠️ WARNING: for...in loops over KEYS (indices), not values
// for (let i in fruits) β†’ "0", "1", "2" (strings, not items)
// Always use for...of for arrays!
// C#: foreach (dedicated keyword)
string[] fruits = { "apple", "banana", "cherry" };

foreach (string fruit in fruits)
{
    Console.WriteLine($"I like {fruit}!");
}

// Need the index too? Use a regular for loop:
for (int i = 0; i < fruits.Length; i++)
{
    Console.WriteLine($"{i}: {fruits[i]}");
}

// Or use LINQ (more advanced):
// foreach (var (fruit, index) in fruits.Select((f, i) => (f, i)))
// {
//     Console.WriteLine($"{index}: {fruit}");
// }

πŸ’‘ Pattern Recognition: JS/C# Share a Family

Notice the trend from the conditionals lesson continuing here: JavaScript and C# share syntax DNA (the three-part for loop, curly braces, semicolons). Python takes its own path with for...in range() and for...in collection. In Python, both counting and iterating use the same for x in structure β€” the only difference is what comes after in.

while Loops

A while loop keeps running as long as a condition is true. You use it when you don't know in advance how many iterations you need.

graph TD A["count = 0"] --> B{"count < 5?"} B -->|Yes| C["Print count"] C --> D["count = count + 1"] D --> B B -->|No| E["Loop ends"] style B fill:#f59e0b,stroke:#d97706,color:#fff,stroke-width:2px style C fill:#10b981,stroke:#059669,color:#fff,stroke-width:2px
# Python: while + condition + colon
count = 0

while count < 5:
    print(count)
    count += 1    # Don't forget this! (infinite loop otherwise)

# Output: 0, 1, 2, 3, 4
// JavaScript: while + parentheses + braces
let count = 0;

while (count < 5) {
    console.log(count);
    count++;      // Don't forget this!
}

// Output: 0, 1, 2, 3, 4
// C#: same structure as JavaScript
int count = 0;

while (count < 5)
{
    Console.WriteLine(count);
    count++;      // Don't forget this!
}

// Output: 0, 1, 2, 3, 4

⚠️ The Infinite Loop Trap

If your condition never becomes false, the loop runs forever and your program hangs. The most common cause: forgetting to update the variable that the condition checks. Always make sure something inside the loop moves you closer to the exit condition.

# INFINITE LOOP β€” Don't do this!
# count = 0
# while count < 5:
#     print(count)
#     # Oops β€” count never changes, so count < 5 is always True

A Real Use Case: Input Validation

while loops shine when you're waiting for valid user input β€” you can't know in advance how many tries it will take.

# Keep asking until the user enters a valid number
while True:
    user_input = input("Enter a number between 1 and 10: ")

    if user_input.isdigit():
        number = int(user_input)
        if 1 <= number <= 10:
            break   # Valid! Exit the loop

    print("Invalid input. Try again.")

print(f"You entered: {number}")
// Browser: prompt returns a string (or null)
let number;

while (true) {
    let userInput = prompt("Enter a number between 1 and 10:");

    number = parseInt(userInput);
    if (!isNaN(number) && number >= 1 && number <= 10) {
        break;   // Valid! Exit the loop
    }

    console.log("Invalid input. Try again.");
}

console.log(`You entered: ${number}`);
int number;

while (true)
{
    Console.Write("Enter a number between 1 and 10: ");
    string userInput = Console.ReadLine();

    if (int.TryParse(userInput, out number) && number >= 1 && number <= 10)
    {
        break;   // Valid! Exit the loop
    }

    Console.WriteLine("Invalid input. Try again.");
}

Console.WriteLine($"You entered: {number}");

break and continue

These two keywords give you fine-grained control over loop execution:

  • break β€” immediately exit the loop entirely
  • continue β€” skip the rest of this iteration and jump to the next one
graph TD A["Start loop"] --> B{"Check condition"} B -->|True| C{"Hit continue?"} C -->|Yes| A C -->|No| D{"Hit break?"} D -->|Yes| E["Exit loop"] D -->|No| F["Run remaining
loop body"] F --> A B -->|False| E style C fill:#f59e0b,stroke:#d97706,color:#fff,stroke-width:2px style D fill:#ef4444,stroke:#dc2626,color:#fff,stroke-width:2px
# Find the first negative number in a list
numbers = [4, 7, 2, -3, 8, 1]

for num in numbers:
    if num < 0:
        print(f"Found negative number: {num}")
        break       # Stop searching β€” we found one
    print(f"Checked {num} β€” positive")

# Output:
# Checked 4 β€” positive
# Checked 7 β€” positive
# Checked 2 β€” positive
# Found negative number: -3
let numbers = [4, 7, 2, -3, 8, 1];

for (let num of numbers) {
    if (num < 0) {
        console.log(`Found negative number: ${num}`);
        break;
    }
    console.log(`Checked ${num} β€” positive`);
}
int[] numbers = { 4, 7, 2, -3, 8, 1 };

foreach (int num in numbers)
{
    if (num < 0)
    {
        Console.WriteLine($"Found negative number: {num}");
        break;
    }
    Console.WriteLine($"Checked {num} β€” positive");
}
# Print only odd numbers from 1 to 10
for i in range(1, 11):
    if i % 2 == 0:
        continue   # Skip even numbers
    print(i)

# Output: 1, 3, 5, 7, 9
// Print only odd numbers from 1 to 10
for (let i = 1; i <= 10; i++) {
    if (i % 2 === 0) {
        continue;   // Skip even numbers
    }
    console.log(i);
}
// Print only odd numbers from 1 to 10
for (int i = 1; i <= 10; i++)
{
    if (i % 2 == 0)
    {
        continue;   // Skip even numbers
    }
    Console.WriteLine(i);
}

πŸ’‘ Good News: break and continue Are the Same Everywhere

Unlike many features we've compared, break and continue work identically across Python, JavaScript, and C#. Same keywords, same behavior. One less thing to worry about when switching languages.

Nested Loops

You can put a loop inside another loop. The inner loop runs completely for each iteration of the outer loop. This is useful for grids, tables, and combinations.

# Print a 3Γ—3 coordinate grid
for row in range(3):
    for col in range(3):
        print(f"({row},{col})", end="  ")
    print()   # New line after each row

# Output:
# (0,0)  (0,1)  (0,2)
# (1,0)  (1,1)  (1,2)
# (2,0)  (2,1)  (2,2)
// Print a 3Γ—3 coordinate grid
for (let row = 0; row < 3; row++) {
    let line = "";
    for (let col = 0; col < 3; col++) {
        line += `(${row},${col})  `;
    }
    console.log(line.trim());
}
// Print a 3Γ—3 coordinate grid
for (int row = 0; row < 3; row++)
{
    for (int col = 0; col < 3; col++)
    {
        Console.Write($"({row},{col})  ");
    }
    Console.WriteLine();   // New line after each row
}

⚠️ Nested Loop Performance

A loop inside a loop multiplies the iteration count. A 10Γ—10 nested loop runs 100 times. A 1000Γ—1000 nested loop runs one million times. For beginners this rarely matters, but it's good to be aware that nested loops can get slow with large data. We'll revisit this when we talk about algorithms in later courses.

πŸŽ“ Instructor Note: Delivery Guidance

Nested loops are one of the first "hard" concepts for beginners. Use the grid/coordinate example first because it's visual and easy to trace. Then show the multiplication table exercise as a practical application. Encourage students to trace through nested loops with a table: "outer = 0, inner goes 0,1,2; outer = 1, inner goes 0,1,2; ..." β€” this manual tracing builds understanding much faster than just reading the code.

for vs. while β€” When to Use Which

Use for when… Use while when…
You know exactly how many iterations You don't know how many iterations
You're iterating over a collection You're waiting for a condition to change
Counting from A to B Reading user input until it's valid
Processing each item in a list Running a game loop
"Do this 10 times" "Keep going until the user says stop"

βœ… Rule of Thumb

If you can express the problem as "for each item" or "do this N times," use a for loop. If you can express it as "keep going until…," use a while loop. When in doubt, for is usually the safer choice because it's harder to accidentally create an infinite loop.

Exercises

πŸ‹οΈ Exercise 1: Multiplication Table

Objective: Print a multiplication table for a given number (1 through 10).

Example output for number = 7:

7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
...
7 x 10 = 70
βœ… Solution
number = 7

print(f"Multiplication table for {number}:")
print("-" * 20)

for i in range(1, 11):
    result = number * i
    print(f"{number} x {i:2d} = {result:3d}")
let number = 7;

console.log(`Multiplication table for ${number}:`);
console.log("-".repeat(20));

for (let i = 1; i <= 10; i++) {
    let result = number * i;
    console.log(`${number} x ${String(i).padStart(2)} = ${String(result).padStart(3)}`);
}
int number = 7;

Console.WriteLine($"Multiplication table for {number}:");
Console.WriteLine(new string('-', 20));

for (int i = 1; i <= 10; i++)
{
    int result = number * i;
    Console.WriteLine($"{number} x {i,2} = {result,3}");
}

πŸ‹οΈ Exercise 2: FizzBuzz

Objective: The classic programming challenge! Print numbers 1 to 30, but:

  • For multiples of 3, print "Fizz" instead of the number
  • For multiples of 5, print "Buzz" instead of the number
  • For multiples of both 3 and 5, print "FizzBuzz"

Hint: Check the "both" case first β€” why?

βœ… Solution
for i in range(1, 31):
    if i % 3 == 0 and i % 5 == 0:
        print("FizzBuzz")
    elif i % 3 == 0:
        print("Fizz")
    elif i % 5 == 0:
        print("Buzz")
    else:
        print(i)

# Why check "both" first? Because 15 is divisible by 3 AND 5.
# If we checked "divisible by 3" first, it would print "Fizz"
# and never reach the "both" check.
for (let i = 1; i <= 30; i++) {
    if (i % 3 === 0 && i % 5 === 0) {
        console.log("FizzBuzz");
    } else if (i % 3 === 0) {
        console.log("Fizz");
    } else if (i % 5 === 0) {
        console.log("Buzz");
    } else {
        console.log(i);
    }
}
for (int i = 1; i <= 30; i++)
{
    if (i % 3 == 0 && i % 5 == 0)
        Console.WriteLine("FizzBuzz");
    else if (i % 3 == 0)
        Console.WriteLine("Fizz");
    else if (i % 5 == 0)
        Console.WriteLine("Buzz");
    else
        Console.WriteLine(i);
}

πŸ‹οΈ Exercise 3: Number Guessing Game

Objective: Create a number guessing game using a while loop.

  1. Set a secret number (hardcode it for now)
  2. Loop: ask the user to guess
  3. Tell them "Too high!" or "Too low!"
  4. When they guess correctly, print how many attempts it took
βœ… Solution
secret = 42
attempts = 0

print("I'm thinking of a number between 1 and 100!")

while True:
    guess_input = input("Your guess: ")

    if not guess_input.isdigit():
        print("Please enter a valid number.")
        continue

    guess = int(guess_input)
    attempts += 1

    if guess < secret:
        print("Too low!")
    elif guess > secret:
        print("Too high!")
    else:
        print(f"πŸŽ‰ Correct! You got it in {attempts} attempts!")
        break
let secret = 42;
let attempts = 0;

console.log("I'm thinking of a number between 1 and 100!");

while (true) {
    let guessInput = prompt("Your guess:");
    let guess = parseInt(guessInput);

    if (isNaN(guess)) {
        console.log("Please enter a valid number.");
        continue;
    }

    attempts++;

    if (guess < secret) {
        console.log("Too low!");
    } else if (guess > secret) {
        console.log("Too high!");
    } else {
        console.log(`πŸŽ‰ Correct! You got it in ${attempts} attempts!`);
        break;
    }
}
int secret = 42;
int attempts = 0;

Console.WriteLine("I'm thinking of a number between 1 and 100!");

while (true)
{
    Console.Write("Your guess: ");
    string guessInput = Console.ReadLine();

    if (!int.TryParse(guessInput, out int guess))
    {
        Console.WriteLine("Please enter a valid number.");
        continue;
    }

    attempts++;

    if (guess < secret)
        Console.WriteLine("Too low!");
    else if (guess > secret)
        Console.WriteLine("Too high!");
    else
    {
        Console.WriteLine($"πŸŽ‰ Correct! You got it in {attempts} attempts!");
        break;
    }
}
πŸŽ“ Instructor Note: Delivery Guidance

Exercise 1 (multiplication table) is the warm-up β€” it reinforces counting for loops. Exercise 2 (FizzBuzz) is a programming classic and makes a great class activity; walk through the first few numbers as a group and ask students why order matters in the if/elif chain. Exercise 3 (guessing game) is the star β€” it naturally demonstrates while True with break, uses continue for input validation, and is fun to play. If students finish early, challenge them to add a random number (import random in Python, Math.random() in JS, Random class in C#) instead of hardcoding the secret.

Summary

πŸŽ‰ Key Takeaways

  • for loops are for counting (range() in Python, three-part syntax in JS/C#) and iterating over collections
  • Python uses for x in range() and for x in collection β€” one syntax for both counting and iterating
  • JavaScript and C# share the classic for (init; cond; step) pattern and have for...of / foreach for collections
  • while loops repeat as long as a condition is true β€” perfect when you don't know the iteration count
  • break exits a loop immediately; continue skips to the next iteration
  • Always make sure while loops have an exit condition to avoid infinite loops
  • Use for when you know the count or have a collection; use while when waiting for a condition

πŸš€ What's Next?

You can now make decisions and repeat actions. In the next lesson, we'll add one more tool to your control flow toolkit: logical operators and truthiness β€” how to combine conditions and understand what each language considers "truthy" or "falsy."

🎯 Quick Check

Question 1: What does range(2, 8) produce in Python?

Question 2: What's the difference between break and continue?