Episode 1 — Fundamentals / 1.19 — Conditionals and Loops
1.19.f -- Practice Problems
In one sentence: Hands-on coding problems that combine conditionals, loops, break/continue, and nested iteration -- each with a problem statement, hints, step-by-step walkthrough, complete solution, and explanation.
Navigation: <-- 1.19.e -- Break and Continue . 1.19 Overview
How to use this material
- Read the problem statement and try to solve it yourself for at least 10 minutes.
- If stuck, read the hints (without looking at the solution).
- Compare your approach with the step-by-step walkthrough.
- Study the code and explanation to understand the pattern.
- Modify the solution (different input, edge cases) to deepen understanding.
Problem 1 -- Prime Number Check
Problem statement
Write a function isPrime(n) that returns true if n is a prime number and false otherwise. A prime number is greater than 1 and divisible only by 1 and itself.
Hints
- Numbers less than 2 are not prime.
- You only need to check divisors up to the square root of
n. Ifn = a * band bothaandbare greater than the square root, thena * b > n-- contradiction. - 2 is the only even prime.
Step-by-step walkthrough
- Handle edge cases: if
n < 2, returnfalse. - Handle
n === 2(prime) and even numbers > 2 (not prime) as quick checks. - Loop from 3 to
Math.sqrt(n), stepping by 2 (skip even divisors). - If any divisor divides
nevenly, returnfalse. - If the loop finishes without finding a divisor, return
true.
Solution
function isPrime(n) {
if (n < 2) return false;
if (n === 2) return true;
if (n % 2 === 0) return false;
for (let i = 3; i <= Math.sqrt(n); i += 2) {
if (n % i === 0) return false;
}
return true;
}
// Test
console.log(isPrime(1)); // false
console.log(isPrime(2)); // true
console.log(isPrime(17)); // true
console.log(isPrime(18)); // false
console.log(isPrime(97)); // true
Explanation
- We eliminate obvious non-primes early (< 2, even numbers).
- Checking up to
Math.sqrt(n)reduces the work from O(n) to O(sqrt(n)). - Stepping by 2 (only odd divisors) cuts the remaining work in half.
- The loop uses an early
return false(which is abreak+ return pattern) the moment a divisor is found.
Problem 2 -- Multiplication Table
Problem statement
Print a multiplication table from 1 to n (given as input), formatted in aligned columns.
Hints
- Use two nested loops: outer for rows, inner for columns.
- Use
String.padStart()to align numbers.
Step-by-step walkthrough
- Outer loop: iterate
ifrom 1 ton. - Inner loop: iterate
jfrom 1 ton. - For each cell, compute
i * jand pad it for alignment. - After the inner loop, print the row.
Solution
function multiplicationTable(n) {
// Header row
let header = " ";
for (let j = 1; j <= n; j++) {
header += String(j).padStart(5);
}
console.log(header);
console.log(" " + "-".repeat(n * 5));
// Data rows
for (let i = 1; i <= n; i++) {
let row = String(i).padStart(3) + " |";
for (let j = 1; j <= n; j++) {
row += String(i * j).padStart(5);
}
console.log(row);
}
}
multiplicationTable(5);
Output
1 2 3 4 5
-------------------------
1 | 1 2 3 4 5
2 | 2 4 6 8 10
3 | 3 6 9 12 15
4 | 4 8 12 16 20
5 | 5 10 15 20 25
Explanation
- The outer loop handles each row (multiplier
i). - The inner loop computes each product for that row.
padStart(5)ensures columns align even when numbers have different digit counts.- This is O(n^2) -- each cell is computed once.
Problem 3 -- Pattern Printing
3a. Right triangle
*
**
***
****
*****
function rightTriangle(rows) {
for (let i = 1; i <= rows; i++) {
let line = "";
for (let j = 0; j < i; j++) {
line += "*";
}
console.log(line);
}
}
rightTriangle(5);
3b. Inverted triangle
*****
****
***
**
*
function invertedTriangle(rows) {
for (let i = rows; i >= 1; i--) {
let line = "";
for (let j = 0; j < i; j++) {
line += "*";
}
console.log(line);
}
}
invertedTriangle(5);
3c. Centered pyramid
*
***
*****
*******
*********
function pyramid(rows) {
for (let i = 1; i <= rows; i++) {
const spaces = " ".repeat(rows - i);
const stars = "*".repeat(2 * i - 1);
console.log(spaces + stars);
}
}
pyramid(5);
3d. Diamond
*
***
*****
*******
*********
*******
*****
***
*
function diamond(n) {
// Top half (including middle row)
for (let i = 1; i <= n; i++) {
const spaces = " ".repeat(n - i);
const stars = "*".repeat(2 * i - 1);
console.log(spaces + stars);
}
// Bottom half
for (let i = n - 1; i >= 1; i--) {
const spaces = " ".repeat(n - i);
const stars = "*".repeat(2 * i - 1);
console.log(spaces + stars);
}
}
diamond(5);
Explanation
- Right triangle: Row
ihasistars. Inner loop builds the string. - Inverted: Same idea, but count down from
rowsto 1. - Pyramid: Row
ineeds(rows - i)leading spaces and(2*i - 1)stars. - Diamond: A pyramid on top, then the same pattern in reverse (minus the middle row to avoid duplication).
- Using
" ".repeat()and"*".repeat()is cleaner than inner loops for building strings.
Problem 4 -- FizzBuzz
Problem statement
Print numbers from 1 to n. For multiples of 3 print "Fizz", for multiples of 5 print "Buzz", for multiples of both print "FizzBuzz".
Hints
- Check divisibility by both 3 and 5 first (15), then by 3, then by 5.
- Order matters in
if-elsechains -- most specific condition first.
Solution
function fizzBuzz(n) {
for (let i = 1; i <= n; 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);
}
}
}
fizzBuzz(20);
Alternative: string-building approach
function fizzBuzzAlt(n) {
for (let i = 1; i <= n; i++) {
let output = "";
if (i % 3 === 0) output += "Fizz";
if (i % 5 === 0) output += "Buzz";
console.log(output || i);
}
}
Explanation
- The string-building approach is more extensible -- if you add "Jazz" for multiples of 7, you just add one more
ifline. output || iprints the number whenoutputis an empty string (falsy).- This is one of the most common interview screening questions. Master both approaches.
Problem 5 -- Fibonacci Sequence Generator
Problem statement
Generate the first n numbers of the Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, ...
Each number is the sum of the two preceding ones.
Hints
- Start with
a = 0andb = 1. - On each iteration, compute
next = a + b, then shift:a = b,b = next.
Solution
function fibonacci(n) {
if (n <= 0) return [];
if (n === 1) return [0];
const result = [0, 1];
for (let i = 2; i < n; i++) {
const next = result[i - 1] + result[i - 2];
result.push(next);
}
return result;
}
console.log(fibonacci(10));
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Alternative: without storing the full array
function printFibonacci(n) {
let a = 0;
let b = 1;
for (let i = 0; i < n; i++) {
console.log(a);
const next = a + b;
a = b;
b = next;
}
}
printFibonacci(10);
// 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
Explanation
- The array approach stores all values (useful if you need them later).
- The two-variable approach uses O(1) space (just
aandb). - The variable swap pattern (
a = b; b = next) is a fundamental technique. - This is O(n) time -- each Fibonacci number is computed once.
Problem 6 -- Sum of Digits
Problem statement
Given a positive integer, calculate the sum of its digits. Example: digitSum(1234) returns 10 (1+2+3+4).
Hints
- Use
% 10to extract the last digit. - Use
Math.floor(n / 10)to remove the last digit. - Repeat until
nbecomes 0.
Solution
function digitSum(n) {
n = Math.abs(n); // handle negative numbers
let sum = 0;
while (n > 0) {
sum += n % 10; // extract last digit
n = Math.floor(n / 10); // remove last digit
}
return sum;
}
console.log(digitSum(1234)); // 10
console.log(digitSum(9999)); // 36
console.log(digitSum(100)); // 1
console.log(digitSum(0)); // 0
Alternative: string approach
function digitSumStr(n) {
return String(Math.abs(n))
.split("")
.reduce((sum, digit) => sum + Number(digit), 0);
}
Explanation
n % 10gives the ones digit (e.g.,1234 % 10 = 4).Math.floor(n / 10)removes the ones digit (e.g.,1234 -> 123).- The
whileloop continues until all digits are extracted (nbecomes 0). - The string approach is shorter but allocates an array; the math approach is pure computation.
Problem 7 -- Reverse a Number
Problem statement
Reverse the digits of an integer. Example: reverseNumber(1234) returns 4321. Handle negative numbers.
Hints
- Same digit-extraction technique as Sum of Digits.
- Build the reversed number:
reversed = reversed * 10 + digit.
Solution
function reverseNumber(n) {
const sign = n < 0 ? -1 : 1;
n = Math.abs(n);
let reversed = 0;
while (n > 0) {
const digit = n % 10;
reversed = reversed * 10 + digit;
n = Math.floor(n / 10);
}
return reversed * sign;
}
console.log(reverseNumber(1234)); // 4321
console.log(reverseNumber(-567)); // -765
console.log(reverseNumber(1000)); // 1 (leading zeros dropped)
console.log(reverseNumber(0)); // 0
Step-by-step trace for reverseNumber(1234)
| Iteration | n | digit | reversed |
|---|---|---|---|
| Start | 1234 | -- | 0 |
| 1 | 123 | 4 | 0*10 + 4 = 4 |
| 2 | 12 | 3 | 4*10 + 3 = 43 |
| 3 | 1 | 2 | 43*10 + 2 = 432 |
| 4 | 0 | 1 | 432*10 + 1 = 4321 |
Explanation
reversed * 10shifts existing digits left (like adding a zero at the end).- Adding the new
digitplaces it in the ones position. - The sign is preserved separately and applied at the end.
Problem 8 -- Palindrome Check
Problem statement
Check if a number is a palindrome (reads the same forwards and backwards). Example: isPalindrome(121) returns true.
Hints
- Negative numbers are not palindromes (the
-sign). - Reverse the number (Problem 7) and compare.
- Or convert to string and compare with its reverse.
Solution (numeric approach)
function isPalindrome(n) {
if (n < 0) return false;
const original = n;
let reversed = 0;
while (n > 0) {
reversed = reversed * 10 + (n % 10);
n = Math.floor(n / 10);
}
return original === reversed;
}
console.log(isPalindrome(121)); // true
console.log(isPalindrome(123)); // false
console.log(isPalindrome(1221)); // true
console.log(isPalindrome(-121)); // false
console.log(isPalindrome(0)); // true
Solution (string approach)
function isPalindromeStr(n) {
const str = String(n);
let left = 0;
let right = str.length - 1;
while (left < right) {
if (str[left] !== str[right]) return false;
left++;
right--;
}
return true;
}
Explanation
- The numeric approach reverses the entire number and compares. No string allocation.
- The string approach uses two pointers moving inward -- it can short-circuit (return
falseearly) on the first mismatch. - Both are O(d) where d is the number of digits.
- The string approach also works for string palindromes (e.g., "racecar") with minor modification.
Problem 9 -- Count Primes Up to N (Bonus)
Problem statement
Count how many prime numbers are less than or equal to n.
Solution (using our isPrime from Problem 1)
function countPrimes(n) {
let count = 0;
for (let i = 2; i <= n; i++) {
if (isPrime(i)) count++;
}
return count;
}
console.log(countPrimes(10)); // 4 (2, 3, 5, 7)
console.log(countPrimes(30)); // 10
console.log(countPrimes(100)); // 25
Advanced: Sieve of Eratosthenes
function sieveOfEratosthenes(n) {
const isPrime = new Array(n + 1).fill(true);
isPrime[0] = isPrime[1] = false;
for (let i = 2; i <= Math.sqrt(n); i++) {
if (isPrime[i]) {
for (let j = i * i; j <= n; j += i) {
isPrime[j] = false; // mark multiples as not prime
}
}
}
const primes = [];
for (let i = 2; i <= n; i++) {
if (isPrime[i]) primes.push(i);
}
return primes;
}
console.log(sieveOfEratosthenes(30));
// [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
Explanation
- The sieve is much faster for large
n-- O(n log log n) vs O(n * sqrt(n)). - It marks all multiples of each prime starting from
i*i(smaller multiples were already marked by smaller primes). - This is a classic example of nested loops with a
continue-like optimization (theif (isPrime[i])guard).
Summary of patterns used
| Pattern | Problems |
|---|---|
| Guard clause / early return | Prime check, palindrome |
while loop with math operations | Digit sum, reverse number, palindrome |
Nested for loops | Multiplication table, patterns, sieve |
| String building in a loop | Patterns, FizzBuzz |
| Two-pointer technique | Palindrome (string approach) |
break for early exit | Prime check (inner loop) |
Navigation: <-- 1.19.e -- Break and Continue . 1.19 Overview