javascript

exercises

exercises.js
/**
 * =====================================================
 * 5.2 FUNCTION EXPRESSIONS - EXERCISES
 * =====================================================
 * Practice function expressions
 */

/**
 * Exercise 1: Basic Function Expression
 *
 * Convert this function declaration to a function expression.
 * function multiply(a, b) { return a * b; }
 */
// TODO: Create function expression
const multiply = undefined;

// Test cases:
console.log('Exercise 1:');
// console.log(multiply(4, 5));   // 20
// console.log(multiply(3, 7));   // 21

/**
 * Exercise 2: Named Function Expression
 *
 * Create a named function expression called 'countdown'
 * that recursively counts down from n to 0.
 * Use the internal name for recursion.
 */
const countdown = undefined;

// Test cases:
console.log('\nExercise 2:');
// countdown(5);  // Should log: 5, 4, 3, 2, 1, 0

/**
 * Exercise 3: IIFE
 *
 * Create an IIFE that returns the sum of 1 to 10.
 */
const sumOneToTen = undefined;

// Test cases:
console.log('\nExercise 3:');
// console.log(sumOneToTen);  // 55

/**
 * Exercise 4: Counter Module
 *
 * Create a counter module using IIFE that has:
 * - Private count variable starting at 0
 * - increment() method
 * - decrement() method
 * - getCount() method
 * - reset() method
 */
const myCounter = undefined;

// Test cases:
console.log('\nExercise 4:');
// console.log(myCounter.getCount());   // 0
// myCounter.increment();
// myCounter.increment();
// console.log(myCounter.getCount());   // 2
// myCounter.decrement();
// console.log(myCounter.getCount());   // 1
// myCounter.reset();
// console.log(myCounter.getCount());   // 0

/**
 * Exercise 5: Create Multiplier
 *
 * Create a function expression that returns another function.
 * The returned function multiplies its argument by the original factor.
 */
const createMultiplier = undefined;

// Test cases:
console.log('\nExercise 5:');
// const double = createMultiplier(2);
// const triple = createMultiplier(3);
// console.log(double(5));   // 10
// console.log(triple(5));   // 15

/**
 * Exercise 6: Callback Processor
 *
 * Create a function expression that takes an array and a callback.
 * Apply the callback to each element and return the new array.
 */
const processArray = undefined;

// Test cases:
console.log('\nExercise 6:');
// const nums = [1, 2, 3, 4, 5];
// const squared = processArray(nums, function(x) { return x * x; });
// console.log(squared);  // [1, 4, 9, 16, 25]

/**
 * Exercise 7: Conditional Formatter
 *
 * Create a function that returns different formatter functions
 * based on the format type: "upper", "lower", "title", "reverse"
 */
const createFormatter = undefined;

// Test cases:
console.log('\nExercise 7:');
// const upper = createFormatter("upper");
// const lower = createFormatter("lower");
// const title = createFormatter("title");
// const reverse = createFormatter("reverse");
// console.log(upper("hello"));     // "HELLO"
// console.log(lower("HELLO"));     // "hello"
// console.log(title("hello world")); // "Hello World"
// console.log(reverse("hello"));   // "olleh"

/**
 * Exercise 8: Once Function
 *
 * Create a function expression that returns a function
 * that can only be called once. Subsequent calls return
 * the same result from the first call.
 */
const once = undefined;

// Test cases:
console.log('\nExercise 8:');
// const initialize = once(function() {
//     console.log("Initializing...");
//     return { status: "ready" };
// });
// console.log(initialize());  // logs "Initializing...", returns { status: "ready" }
// console.log(initialize());  // just returns { status: "ready" }, no log

/**
 * Exercise 9: Compose Functions
 *
 * Create a compose function that takes two functions
 * and returns a new function that applies them right-to-left.
 * compose(f, g)(x) = f(g(x))
 */
const compose = undefined;

// Test cases:
console.log('\nExercise 9:');
// const addOne = function(x) { return x + 1; };
// const double = function(x) { return x * 2; };
// const addOneThenDouble = compose(double, addOne);
// console.log(addOneThenDouble(5));  // 12 (5+1=6, 6*2=12)

/**
 * Exercise 10: Simple Event Emitter
 *
 * Create an event emitter using IIFE with:
 * - on(event, handler) - register handler
 * - emit(event, data) - call all handlers for event
 * - off(event, handler) - remove specific handler
 */
const eventEmitter = undefined;

// Test cases:
console.log('\nExercise 10:');
// const handler1 = function(data) { console.log("Handler 1:", data); };
// const handler2 = function(data) { console.log("Handler 2:", data); };
// eventEmitter.on("test", handler1);
// eventEmitter.on("test", handler2);
// eventEmitter.emit("test", "Hello!");  // Both handlers called
// eventEmitter.off("test", handler1);
// eventEmitter.emit("test", "Goodbye!");  // Only handler2 called

// =====================================================
// INTERMEDIATE EXERCISES
// =====================================================

/**
 * Exercise 11: Memoize
 *
 * Create a memoize function expression that caches
 * results of function calls.
 */
const memoize = undefined;

// Test cases:
console.log('\nExercise 11:');
// let callCount = 0;
// const expensiveOperation = function(n) {
//     callCount++;
//     return n * n;
// };
// const memoizedOp = memoize(expensiveOperation);
// console.log(memoizedOp(5));  // 25, callCount = 1
// console.log(memoizedOp(5));  // 25, callCount = 1 (cached)
// console.log(memoizedOp(6));  // 36, callCount = 2

/**
 * Exercise 12: Debounce
 *
 * Create a debounce function that delays invoking a function
 * until after X milliseconds have passed since the last call.
 */
const debounce = undefined;

// Test cases:
console.log('\nExercise 12:');
// const log = debounce(function(msg) {
//     console.log("Debounced:", msg);
// }, 100);
// log("a"); log("b"); log("c");
// // Only "c" should be logged after 100ms

/**
 * Exercise 13: Pipe
 *
 * Create a pipe function that takes multiple functions
 * and applies them left-to-right (opposite of compose).
 * pipe(f, g, h)(x) = h(g(f(x)))
 */
const pipe = undefined;

// Test cases:
console.log('\nExercise 13:');
// const addTwo = function(x) { return x + 2; };
// const multiplyThree = function(x) { return x * 3; };
// const subtractOne = function(x) { return x - 1; };
// const piped = pipe(addTwo, multiplyThree, subtractOne);
// console.log(piped(5));  // 20 ((5+2)*3-1)

/**
 * Exercise 14: Partial Application
 *
 * Create a partial function that pre-fills some arguments.
 */
const partial = undefined;

// Test cases:
console.log('\nExercise 14:');
// const greet = function(greeting, name) {
//     return greeting + ", " + name + "!";
// };
// const sayHello = partial(greet, "Hello");
// console.log(sayHello("Alice"));  // "Hello, Alice!"

/**
 * Exercise 15: Create Store
 *
 * Create a simple state store using IIFE with:
 * - getState() - returns current state
 * - setState(newState) - updates state
 * - subscribe(listener) - adds change listener
 * - unsubscribe(listener) - removes listener
 */
const createStore = undefined;

// Test cases:
console.log('\nExercise 15:');
// const store = createStore({ count: 0 });
// const listener = function(state) { console.log("State changed:", state); };
// store.subscribe(listener);
// store.setState({ count: 1 });  // Should log state change
// console.log(store.getState()); // { count: 1 }
// store.unsubscribe(listener);
// store.setState({ count: 2 });  // No log (unsubscribed)

// =====================================================
// SOLUTIONS (Uncomment to check your answers)
// =====================================================

/*
// Exercise 1 Solution:
const multiply = function(a, b) {
    return a * b;
};

// Exercise 2 Solution:
const countdown = function countdownRecursive(n) {
    console.log(n);
    if (n > 0) {
        countdownRecursive(n - 1);
    }
};

// Exercise 3 Solution:
const sumOneToTen = (function() {
    let sum = 0;
    for (let i = 1; i <= 10; i++) {
        sum += i;
    }
    return sum;
})();

// Exercise 4 Solution:
const myCounter = (function() {
    let count = 0;
    return {
        increment: function() { count++; return this; },
        decrement: function() { count--; return this; },
        getCount: function() { return count; },
        reset: function() { count = 0; return this; }
    };
})();

// Exercise 5 Solution:
const createMultiplier = function(factor) {
    return function(number) {
        return number * factor;
    };
};

// Exercise 6 Solution:
const processArray = function(arr, callback) {
    const result = [];
    for (const item of arr) {
        result.push(callback(item));
    }
    return result;
};

// Exercise 7 Solution:
const createFormatter = function(type) {
    switch (type) {
        case "upper":
            return function(str) { return str.toUpperCase(); };
        case "lower":
            return function(str) { return str.toLowerCase(); };
        case "title":
            return function(str) {
                return str.split(" ").map(function(word) {
                    return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
                }).join(" ");
            };
        case "reverse":
            return function(str) { return str.split("").reverse().join(""); };
        default:
            return function(str) { return str; };
    }
};

// Exercise 8 Solution:
const once = function(fn) {
    let called = false;
    let result;
    return function(...args) {
        if (!called) {
            called = true;
            result = fn(...args);
        }
        return result;
    };
};

// Exercise 9 Solution:
const compose = function(f, g) {
    return function(x) {
        return f(g(x));
    };
};

// Exercise 10 Solution:
const eventEmitter = (function() {
    const events = {};
    return {
        on: function(event, handler) {
            if (!events[event]) events[event] = [];
            events[event].push(handler);
        },
        emit: function(event, data) {
            if (events[event]) {
                events[event].forEach(function(h) { h(data); });
            }
        },
        off: function(event, handler) {
            if (events[event]) {
                events[event] = events[event].filter(function(h) {
                    return h !== handler;
                });
            }
        }
    };
})();

// Exercise 11 Solution:
const memoize = function(fn) {
    const cache = {};
    return function(...args) {
        const key = JSON.stringify(args);
        if (cache[key] !== undefined) {
            return cache[key];
        }
        const result = fn(...args);
        cache[key] = result;
        return result;
    };
};

// Exercise 12 Solution:
const debounce = function(fn, delay) {
    let timeoutId;
    return function(...args) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(function() {
            fn(...args);
        }, delay);
    };
};

// Exercise 13 Solution:
const pipe = function(...fns) {
    return function(x) {
        return fns.reduce(function(acc, fn) {
            return fn(acc);
        }, x);
    };
};

// Exercise 14 Solution:
const partial = function(fn, ...presetArgs) {
    return function(...laterArgs) {
        return fn(...presetArgs, ...laterArgs);
    };
};

// Exercise 15 Solution:
const createStore = function(initialState) {
    let state = initialState;
    const listeners = [];
    
    return {
        getState: function() { return state; },
        setState: function(newState) {
            state = newState;
            listeners.forEach(function(listener) {
                listener(state);
            });
        },
        subscribe: function(listener) {
            listeners.push(listener);
        },
        unsubscribe: function(listener) {
            const index = listeners.indexOf(listener);
            if (index > -1) {
                listeners.splice(index, 1);
            }
        }
    };
};
*/
Exercises - JavaScript Tutorial | DeepML