Module 4: Loops, Conditionals & Control Flow Practice Problems

Where "repeat this" and "decide that" turn into working programs — patterns, tables, counting drills and the small conditional puzzles (like leap years) that trip people up precisely because the rule sounds simpler than it is. Every problem leans on loops and if/else you already know; the skill being built is combining them cleanly.

Module 4 of 12 Problems 31–40 JS + Python ~45–60 Min

By the end of this module, you'll be able to

  • Chain if/else-if branches in the right order so the most specific case is checked first
  • Build a pattern or table with a nested loop: an outer loop for rows, an inner loop for columns
  • Combine &&/and and ||/or correctly to express a rule with more than one condition

1. Problems 31–40

Same format as the previous modules: expand a problem to see the approach and both commented solutions.

P31

FizzBuzz

1 to 15: replace multiples of 3 with "Fizz", of 5 with "Buzz", of both with "FizzBuzz".

Approach: check the most specific condition first — divisible by both 3 and 5 (equivalently, by 15) — before falling back to the individual checks, otherwise a multiple of 15 would incorrectly print only "Fizz".

JavaScript
fizz-buzz.js
function fizzBuzz(n) {
  const output = [];
  for (let i = 1; i <= n; i++) {
    if (i % 15 === 0) output.push("FizzBuzz"); // divisible by both 3 and 5
    else if (i % 3 === 0) output.push("Fizz");
    else if (i % 5 === 0) output.push("Buzz");
    else output.push(String(i));
  }
  return output;
}

console.log(fizzBuzz(15).join(" "));
// 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz
Python
fizz_buzz.py
def fizz_buzz(n: int) -> list:
    output = []
    for i in range(1, n + 1):
        if i % 15 == 0:      # divisible by both 3 and 5
            output.append("FizzBuzz")
        elif i % 3 == 0:
            output.append("Fizz")
        elif i % 5 == 0:
            output.append("Buzz")
        else:
            output.append(str(i))
    return output

print(" ".join(fizz_buzz(15)))
# 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz
P32

Right-Angled Triangle of Stars

5 rows: row i gets i stars.

Approach: loop from row 1 to the total row count, and on row i repeat the "*" character i times before moving to the next line.

JavaScript
star-triangle.js
function starTriangle(rows) {
  let output = "";
  for (let i = 1; i <= rows; i++) {
    output += "*".repeat(i) + "\n"; // row i gets i stars
  }
  return output;
}

console.log(starTriangle(5));
// *
// **
// ***
// ****
// *****
Python
star_triangle.py
def star_triangle(rows: int) -> str:
    output = ""
    for i in range(1, rows + 1):
        output += "*" * i + "\n"  # row i gets i stars
    return output

print(star_triangle(5))
# *
# **
# ***
# ****
# *****
P33

Sum of Numbers from 1 to n

1 + 2 + ... + 10 = 55.

Approach: the simplest possible accumulator loop — start a running total at 0 and add each number from 1 to n as you go.

JavaScript
sum-to-n.js
function sumToN(n) {
  let sum = 0;
  for (let i = 1; i <= n; i++) {
    sum += i;
  }
  return sum;
}

console.log(sumToN(10)); // 55
Python
sum_to_n.py
def sum_to_n(n: int) -> int:
    total = 0
    for i in range(1, n + 1):
        total += i
    return total

print(sum_to_n(10))  # 55
P34

Count Even and Odd Numbers in a Range

1 to 10 has 5 evens and 5 odds.

Approach: loop through the range once, and on each number check % 2 to decide which of two counters to increment.

JavaScript
count-even-odd.js
function countEvenOdd(n) {
  let evens = 0, odds = 0;
  for (let i = 1; i <= n; i++) {
    if (i % 2 === 0) evens++;
    else odds++;
  }
  return { evens, odds };
}

console.log(countEvenOdd(10)); // { evens: 5, odds: 5 }
Python
count_even_odd.py
def count_even_odd(n: int) -> dict:
    evens = odds = 0
    for i in range(1, n + 1):
        if i % 2 == 0:
            evens += 1
        else:
            odds += 1
    return {"evens": evens, "odds": odds}

print(count_even_odd(10))  # {'evens': 5, 'odds': 5}
P35

All Prime Numbers up to n

Up to 20: 2, 3, 5, 7, 11, 13, 17, 19.

Approach: the outer loop tries every candidate number; the inner loop reruns the Module 2 primality check against it. A nested loop is exactly "for each X, check every Y."

JavaScript
primes-up-to.js
function primesUpTo(n) {
  const primes = [];
  for (let num = 2; num <= n; num++) {
    let prime = true;
    for (let i = 2; i * i <= num; i++) {
      if (num % i === 0) { prime = false; break; } // found a divisor
    }
    if (prime) primes.push(num);
  }
  return primes;
}

console.log(primesUpTo(20)); // [2, 3, 5, 7, 11, 13, 17, 19]
Python
primes_up_to.py
def primes_up_to(n: int) -> list:
    primes = []
    for num in range(2, n + 1):
        prime = True
        i = 2
        while i * i <= num:
            if num % i == 0:  # found a divisor
                prime = False
                break
            i += 1
        if prime:
            primes.append(num)
    return primes

print(primes_up_to(20))  # [2, 3, 5, 7, 11, 13, 17, 19]
P36

Print a Multiplication Table

7 x 1 through 7 x 5.

Approach: loop from 1 up to however many rows are wanted, and on each pass print n x i = n * i using string interpolation.

JavaScript
multiplication-table.js
function multiplicationTable(n, upTo = 10) {
  let output = "";
  for (let i = 1; i <= upTo; i++) {
    output += `${n} x ${i} = ${n * i}\n`;
  }
  return output;
}

console.log(multiplicationTable(7, 5));
// 7 x 1 = 7
// 7 x 2 = 14
// 7 x 3 = 21
// 7 x 4 = 28
// 7 x 5 = 35
Python
multiplication_table.py
def multiplication_table(n: int, up_to: int = 10) -> str:
    output = ""
    for i in range(1, up_to + 1):
        output += f"{n} x {i} = {n * i}\n"
    return output

print(multiplication_table(7, 5))
# 7 x 1 = 7
# 7 x 2 = 14
# 7 x 3 = 21
# 7 x 4 = 28
# 7 x 5 = 35
P37

Largest of Three Numbers

Of 4, 9 and 7, the largest is 9.

Approach: a is the largest if it's greater than or equal to both others; otherwise check b the same way; if neither wins, c must be the largest. No sorting needed for just three values.

JavaScript
largest-of-three.js
function largestOfThree(a, b, c) {
  if (a >= b && a >= c) return a;
  if (b >= a && b >= c) return b;
  return c;
}

console.log(largestOfThree(4, 9, 7)); // 9
Python
largest_of_three.py
def largest_of_three(a: float, b: float, c: float) -> float:
    if a >= b and a >= c:
        return a
    if b >= a and b >= c:
        return b
    return c

print(largest_of_three(4, 9, 7))  # 9
# Idiomatic one-liner for real code: max(a, b, c)
P38

Leap Year Checker

2024 and 2000 are leap years; 2100 isn't.

Approach: the actual rule has three parts: divisible by 4, unless also divisible by 100, unless also divisible by 400. Written as one boolean expression: divisible by 4 AND (not divisible by 100 OR divisible by 400).

JavaScript
is-leap-year.js
function isLeapYear(year) {
  // Divisible by 4 AND (not divisible by 100 OR divisible by 400)
  return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
}

console.log(isLeapYear(2024)); // true
console.log(isLeapYear(2100)); // false (divisible by 100, not by 400)
console.log(isLeapYear(2000)); // true (divisible by 400)
Python
is_leap_year.py
def is_leap_year(year: int) -> bool:
    # Divisible by 4 AND (not divisible by 100 OR divisible by 400)
    return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)

print(is_leap_year(2024))  # True
print(is_leap_year(2100))  # False (divisible by 100, not by 400)
print(is_leap_year(2000))  # True (divisible by 400)
P39

Count the Digits of a Number

48213 has 5 digits.

Approach: the same digit-peeling while loop from Module 2, but this time only counting how many times it runs rather than doing anything with each digit. Zero needs its own special case, since the loop would otherwise never run for it.

JavaScript
count-digits.js
function countDigits(n) {
  n = Math.abs(n);
  if (n === 0) return 1; // zero itself has one digit
  let count = 0;
  while (n > 0) {
    count++;
    n = Math.floor(n / 10); // drop the last digit each pass
  }
  return count;
}

console.log(countDigits(48213)); // 5
Python
count_digits.py
def count_digits(n: int) -> int:
    n = abs(n)
    if n == 0:  # zero itself has one digit
        return 1
    count = 0
    while n > 0:
        count += 1
        n //= 10  # drop the last digit each pass
    return count

print(count_digits(48213))  # 5
P40

Number Pyramid Pattern

Row i counts up from 1 to i: 1 / 12 / 123 / 1234 / 12345.

Approach: a nested loop, like problem 35's prime sieve — the outer loop picks the row number, the inner loop counts from 1 up to that row number and appends each digit.

JavaScript
number-pyramid.js
function numberPyramid(rows) {
  let output = "";
  for (let i = 1; i <= rows; i++) {
    let row = "";
    for (let j = 1; j <= i; j++) {
      row += j; // count up to the row number on each row
    }
    output += row + "\n";
  }
  return output;
}

console.log(numberPyramid(5));
// 1
// 12
// 123
// 1234
// 12345
Python
number_pyramid.py
def number_pyramid(rows: int) -> str:
    output = ""
    for i in range(1, rows + 1):
        row = ""
        for j in range(1, i + 1):
            row += str(j)  # count up to the row number on each row
        output += row + "\n"
    return output

print(number_pyramid(5))
# 1
# 12
# 123
# 1234
# 12345

2. Key Takeaways

  • Order matters in an if/else-if chain — always check the most specific or most restrictive condition first (FizzBuzz's "divisible by 15" before "divisible by 3").
  • A nested loop is just "for each row, do a smaller loop" — that one idea builds triangles, pyramids, multiplication tables and the prime sieve identically.
  • Compound conditions (leap years) read most clearly when you write out the plain-English rule as a comment first, then translate it term by term into &&/and and ||/or.