Control Structures: Conditionals and Loops

Week 1, Day 5 - Lecture 2

Control Flow in Programming

Control structures are like traffic signals for your code - they determine which path your program takes and how many times it travels that path. They allow your programs to make decisions and repeat actions, making them dynamic and intelligent!

graph TD A[Program Start] --> B{Condition?} B -->|True| C[Execute Path A] B -->|False| D[Execute Path B] C --> E[Continue] D --> E E --> F{Loop?} F -->|Yes| G[Repeat Actions] G --> F F -->|No| H[Program End] style B fill:#f9f,stroke:#333,stroke-width:2px style F fill:#9cf,stroke:#333,stroke-width:2px

Conditional Statements

if Statement

// Basic if statement
let age = 18;

if (age >= 18) {
    console.log("You are an adult");
}

// if-else statement
if (age >= 18) {
    console.log("You are an adult");
} else {
    console.log("You are a minor");
}

// if-else if-else chain
let score = 85;

if (score >= 90) {
    console.log("Grade: A");
} else if (score >= 80) {
    console.log("Grade: B");
} else if (score >= 70) {
    console.log("Grade: C");
} else if (score >= 60) {
    console.log("Grade: D");
} else {
    console.log("Grade: F");
}

Nested if Statements

let age = 25;
let hasLicense = true;
let hasInsurance = true;

if (age >= 18) {
    if (hasLicense) {
        if (hasInsurance) {
            console.log("You can drive!");
        } else {
            console.log("You need insurance to drive.");
        }
    } else {
        console.log("You need a license to drive.");
    }
} else {
    console.log("You must be 18 or older to drive.");
}

// Better approach with logical operators
if (age >= 18 && hasLicense && hasInsurance) {
    console.log("You can drive!");
} else if (age < 18) {
    console.log("You must be 18 or older to drive.");
} else if (!hasLicense) {
    console.log("You need a license to drive.");
} else if (!hasInsurance) {
    console.log("You need insurance to drive.");
}

Switch Statement

// Basic switch statement
let day = "Monday";

switch (day) {
    case "Monday":
        console.log("Start of the work week");
        break;
    case "Tuesday":
    case "Wednesday":
    case "Thursday":
        console.log("Middle of the work week");
        break;
    case "Friday":
        console.log("End of the work week");
        break;
    case "Saturday":
    case "Sunday":
        console.log("Weekend!");
        break;
    default:
        console.log("Invalid day");
}

// Switch with expressions
let score = 85;

switch (true) {
    case score >= 90:
        console.log("Grade: A");
        break;
    case score >= 80:
        console.log("Grade: B");
        break;
    case score >= 70:
        console.log("Grade: C");
        break;
    case score >= 60:
        console.log("Grade: D");
        break;
    default:
        console.log("Grade: F");
}

Ternary Operator

// Basic ternary operator
let age = 20;
let status = age >= 18 ? "adult" : "minor";

// Nested ternary (use sparingly)
let score = 85;
let grade = score >= 90 ? "A" :
           score >= 80 ? "B" :
           score >= 70 ? "C" :
           score >= 60 ? "D" : "F";

// Multiple actions in ternary
let isLoggedIn = true;
isLoggedIn ? (
    console.log("Welcome back!"),
    showDashboard()
) : (
    console.log("Please log in"),
    showLoginForm()
);

Logical Short-Circuiting

// AND (&&) short-circuiting
let user = null;
user && user.getName(); // Won't execute getName if user is null

// OR (||) short-circuiting for default values
let name = user.name || "Guest";

// Nullish coalescing (??) - only for null/undefined
let count = 0;
let displayCount = count ?? 10; // Uses 0, not 10
let missingValue = undefined;
let defaultValue = missingValue ?? 10; // Uses 10

// Optional chaining (?.)
let user = {
    address: {
        street: "123 Main St"
    }
};

console.log(user.address?.street); // "123 Main St"
console.log(user.phone?.number); // undefined (no error)

// Combining operators
let streetName = user.address?.street ?? "No address provided";

Loops

for Loop

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

// Looping through arrays
let fruits = ["apple", "banana", "orange"];
for (let i = 0; i < fruits.length; i++) {
    console.log(fruits[i]);
}

// Reverse loop
for (let i = fruits.length - 1; i >= 0; i--) {
    console.log(fruits[i]);
}

// Multiple variables
for (let i = 0, j = 10; i < 5; i++, j--) {
    console.log(i, j);
}

// Nested loops
for (let i = 1; i <= 3; i++) {
    for (let j = 1; j <= 3; j++) {
        console.log(`${i} x ${j} = ${i * j}`);
    }
}

while Loop

// Basic while loop
let count = 0;
while (count < 5) {
    console.log(count);
    count++;
}

// Infinite loop with break
while (true) {
    let input = prompt("Enter 'quit' to exit:");
    if (input === 'quit') {
        break;
    }
    console.log("You entered: " + input);
}

// while loop for unknown iterations
let password = "";
while (password !== "secret123") {
    password = prompt("Enter the password:");
}
console.log("Access granted!");

do...while Loop

// Basic do...while loop
let i = 0;
do {
    console.log(i);
    i++;
} while (i < 5);

// Executes at least once
let number = 10;
do {
    console.log("This runs at least once");
} while (number < 5);

// User input validation
let age;
do {
    age = parseInt(prompt("Enter your age (must be positive):"));
} while (isNaN(age) || age <= 0);
console.log("Your age is: " + age);

for...in Loop

// Iterating over object properties
let person = {
    name: "John",
    age: 30,
    city: "New York"
};

for (let key in person) {
    console.log(key + ": " + person[key]);
}

// Works with arrays but not recommended
let colors = ["red", "green", "blue"];
for (let index in colors) {
    console.log(index + ": " + colors[index]); // Logs indices, not values
}

// Checking hasOwnProperty
for (let key in person) {
    if (person.hasOwnProperty(key)) {
        console.log(key + ": " + person[key]);
    }
}

for...of Loop

// Iterating over array values
let fruits = ["apple", "banana", "orange"];
for (let fruit of fruits) {
    console.log(fruit);
}

// Iterating over string characters
let text = "Hello";
for (let char of text) {
    console.log(char);
}

// With array destructuring
let users = [
    ["John", 30],
    ["Jane", 25],
    ["Bob", 35]
];

for (let [name, age] of users) {
    console.log(`${name} is ${age} years old`);
}

// Works with other iterables
let set = new Set([1, 2, 3]);
for (let value of set) {
    console.log(value);
}

let map = new Map([["a", 1], ["b", 2]]);
for (let [key, value] of map) {
    console.log(key + ": " + value);
}

Loop Control Statements

break Statement

// Breaking out of a loop
for (let i = 0; i < 10; i++) {
    if (i === 5) {
        break; // Exit the loop completely
    }
    console.log(i); // 0, 1, 2, 3, 4
}

// Breaking from nested loops
outer: for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
        if (i === 1 && j === 1) {
            break outer; // Breaks both loops
        }
        console.log(i, j);
    }
}

continue Statement

// Skipping iterations
for (let i = 0; i < 5; i++) {
    if (i === 2) {
        continue; // Skip this iteration
    }
    console.log(i); // 0, 1, 3, 4
}

// continue with labels
outer: for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
        if (j === 1) {
            continue outer; // Skip to next outer loop iteration
        }
        console.log(i, j);
    }
}

Common Loop Patterns

Searching Arrays

// Finding an element
let numbers = [1, 3, 5, 7, 9];
let searchValue = 5;
let found = false;

for (let i = 0; i < numbers.length; i++) {
    if (numbers[i] === searchValue) {
        found = true;
        console.log(`Found ${searchValue} at index ${i}`);
        break;
    }
}

if (!found) {
    console.log(`${searchValue} not found`);
}

Filtering Arrays

// Creating a filtered array
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let evenNumbers = [];

for (let num of numbers) {
    if (num % 2 === 0) {
        evenNumbers.push(num);
    }
}

console.log(evenNumbers); // [2, 4, 6, 8, 10]

Accumulating Values

// Sum of array elements
let numbers = [1, 2, 3, 4, 5];
let sum = 0;

for (let num of numbers) {
    sum += num;
}

console.log(sum); // 15

// Finding maximum value
let max = numbers[0];
for (let i = 1; i < numbers.length; i++) {
    if (numbers[i] > max) {
        max = numbers[i];
    }
}
console.log(max); // 5

Practical Examples

Password Validator

function validatePassword(password) {
    let hasUpperCase = false;
    let hasLowerCase = false;
    let hasNumber = false;
    let hasSpecialChar = false;
    
    if (password.length < 8) {
        return "Password must be at least 8 characters long";
    }
    
    for (let char of password) {
        if (char >= 'A' && char <= 'Z') {
            hasUpperCase = true;
        } else if (char >= 'a' && char <= 'z') {
            hasLowerCase = true;
        } else if (char >= '0' && char <= '9') {
            hasNumber = true;
        } else if ("!@#$%^&*".includes(char)) {
            hasSpecialChar = true;
        }
    }
    
    if (!hasUpperCase) return "Password must contain an uppercase letter";
    if (!hasLowerCase) return "Password must contain a lowercase letter";
    if (!hasNumber) return "Password must contain a number";
    if (!hasSpecialChar) return "Password must contain a special character";
    
    return "Password is valid";
}

Shopping Cart Calculator

function calculateTotal(cart) {
    let subtotal = 0;
    let taxRate = 0.08;
    let shippingRate = 5.99;
    
    for (let item of cart) {
        subtotal += item.price * item.quantity;
    }
    
    let tax = subtotal * taxRate;
    let shipping = subtotal > 50 ? 0 : shippingRate;
    let total = subtotal + tax + shipping;
    
    return {
        subtotal: subtotal.toFixed(2),
        tax: tax.toFixed(2),
        shipping: shipping.toFixed(2),
        total: total.toFixed(2)
    };
}

let cart = [
    { name: "Book", price: 15.99, quantity: 2 },
    { name: "Pen", price: 1.99, quantity: 5 },
    { name: "Notebook", price: 5.99, quantity: 3 }
];

console.log(calculateTotal(cart));

Advanced Patterns

State Machine

let state = "idle";
let fuel = 100;

while (fuel > 0) {
    switch (state) {
        case "idle":
            console.log("Engine idle...");
            state = "accelerating";
            break;
        case "accelerating":
            console.log("Speeding up!");
            fuel -= 2;
            if (fuel < 50) {
                state = "cruising";
            }
            break;
        case "cruising":
            console.log("Cruising along...");
            fuel -= 1;
            if (fuel < 20) {
                state = "braking";
            }
            break;
        case "braking":
            console.log("Slowing down...");
            fuel -= 0.5;
            if (fuel < 10) {
                state = "idle";
            }
            break;
    }
}
console.log("Out of fuel!");

Menu System

function showMenu() {
    let choice;
    
    do {
        console.log("\n=== Main Menu ===");
        console.log("1. View Profile");
        console.log("2. Edit Settings");
        console.log("3. Check Messages");
        console.log("4. Exit");
        
        choice = prompt("Enter your choice (1-4):");
        
        switch (choice) {
            case "1":
                console.log("Viewing profile...");
                break;
            case "2":
                console.log("Editing settings...");
                break;
            case "3":
                console.log("Checking messages...");
                break;
            case "4":
                console.log("Goodbye!");
                break;
            default:
                console.log("Invalid choice. Please try again.");
        }
    } while (choice !== "4");
}

Best Practices

mindmap root((Control Structures Best Practices)) Conditionals Use === not == Keep conditions simple Avoid deep nesting Consider early returns Loops Choose right loop type Avoid infinite loops Use meaningful names Consider performance General Write readable code Handle edge cases Test thoroughly Avoid side effects

Code Style Tips

// Bad - Deep nesting
if (condition1) {
    if (condition2) {
        if (condition3) {
            // Do something
        }
    }
}

// Good - Guard clauses
if (!condition1) return;
if (!condition2) return;
if (!condition3) return;
// Do something

// Bad - Complex condition
if (user.age >= 18 && user.hasLicense && user.hasInsurance && !user.suspended) {
    // Allow driving
}

// Good - Extract to function
function canDrive(user) {
    return user.age >= 18 && 
           user.hasLicense && 
           user.hasInsurance && 
           !user.suspended;
}

if (canDrive(user)) {
    // Allow driving
}

Assignment: Control Structures

  1. Create a grade calculator:
    • Accept a numeric score (0-100)
    • Return letter grade (A-F)
    • Handle invalid input
    • Add plus/minus grades (e.g., B+, A-)
  2. Build a number guessing game:
    • Generate random number (1-100)
    • Allow user to guess
    • Give hints (higher/lower)
    • Count number of attempts
    • Implement play again feature
  3. Create a prime number checker:
    • Check if a number is prime
    • Find all primes up to n
    • Optimize for performance
    • Handle edge cases
  4. Implement a menu-driven calculator:
    • Basic arithmetic operations
    • Memory functions (store/recall)
    • Input validation
    • Graceful exit

Bonus: Create a text-based adventure game with multiple paths and decisions!