javascript

exercises

exercises.js
/**
 * =====================================================
 * 5.4 PARAMETERS AND ARGUMENTS - EXERCISES
 * =====================================================
 * Practice function parameters
 */

/**
 * Exercise 1: Default Greeting
 *
 * Create a function with default parameters for both
 * name and greeting.
 */
function greet(name, greeting) {
  // TODO: Add default parameters
  // Default name: "Guest", greeting: "Hello"
}

// Test cases:
console.log('Exercise 1:');
console.log(greet()); // "Hello, Guest!"
console.log(greet('Alice')); // "Hello, Alice!"
console.log(greet('Bob', 'Hi')); // "Hi, Bob!"

/**
 * Exercise 2: Sum All Numbers
 *
 * Create a function using rest parameters that sums
 * any number of arguments.
 */
function sumAll() {
  // TODO: Use rest parameters
}

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

/**
 * Exercise 3: Create User with Options
 *
 * Create a function that accepts an options object
 * with defaults for role, active, and createdAt.
 */
function createUser(options) {
  // TODO: Destructure options with defaults
  // name: required
  // role: "user"
  // active: true
  // createdAt: new Date()
}

// Test cases:
console.log('\nExercise 3:');
console.log(createUser({ name: 'Alice' }));
// { name: "Alice", role: "user", active: true, createdAt: Date }
console.log(createUser({ name: 'Bob', role: 'admin', active: false }));

/**
 * Exercise 4: Array Statistics
 *
 * Create a function using destructuring in parameters
 * that takes [first, ...rest] and returns statistics.
 */
function getStats(numbers) {
  // TODO: Use array destructuring
  // Return { first, last, count, sum }
}

// Test cases:
console.log('\nExercise 4:');
console.log(getStats([1, 2, 3, 4, 5]));
// { first: 1, last: 5, count: 5, sum: 15 }

/**
 * Exercise 5: Merge Objects
 *
 * Create a function that merges multiple objects using rest.
 */
function mergeObjects() {
  // TODO: Use rest parameters and spread
}

// Test cases:
console.log('\nExercise 5:');
console.log(mergeObjects({ a: 1 }, { b: 2 }, { c: 3 }));
// { a: 1, b: 2, c: 3 }
console.log(mergeObjects({ a: 1, b: 2 }, { b: 3, c: 4 }));
// { a: 1, b: 3, c: 4 }

/**
 * Exercise 6: Format Price
 *
 * Create a function with named parameters for formatting prices.
 */
function formatPrice(options) {
  // TODO: Destructure with defaults
  // amount: required
  // currency: "USD"
  // locale: "en-US"
  // decimals: 2
}

// Test cases:
console.log('\nExercise 6:');
console.log(formatPrice({ amount: 1234.5 })); // "$1,234.50"
console.log(formatPrice({ amount: 1234.5, currency: 'EUR', locale: 'de-DE' }));

/**
 * Exercise 7: Required Parameters
 *
 * Create a required() helper and use it in a function.
 */
function required(paramName) {
  // TODO: Throw error with param name
}

function registerUser(username, email, password) {
  // TODO: Make username, email, password required
}

// Test cases:
console.log('\nExercise 7:');
try {
  registerUser('alice', 'alice@example.com'); // Should throw
} catch (e) {
  console.log('Error:', e.message);
}

/**
 * Exercise 8: Clone Without Mutation
 *
 * Create a function that adds properties to an object
 * without mutating the original.
 */
function addProperties(obj, properties) {
  // TODO: Return new object with added properties
}

// Test cases:
console.log('\nExercise 8:');
const original = { a: 1 };
const updated = addProperties(original, { b: 2, c: 3 });
console.log('Original:', original); // { a: 1 }
console.log('Updated:', updated); // { a: 1, b: 2, c: 3 }

/**
 * Exercise 9: Nested Destructuring
 *
 * Create a function that extracts deeply nested values.
 */
function extractUserInfo(response) {
  // TODO: Destructure response.data.user.{name, email}
  // and response.status
}

// Test cases:
console.log('\nExercise 9:');
const apiResponse = {
  status: 200,
  data: {
    user: {
      name: 'Alice',
      email: 'alice@example.com',
      preferences: { theme: 'dark' },
    },
  },
};
console.log(extractUserInfo(apiResponse));
// { name: "Alice", email: "alice@example.com", status: 200 }

/**
 * Exercise 10: Logger Function
 *
 * Create a flexible logger using rest and named parameters.
 */
function createLogger(options) {
  // TODO: Return a function that logs with prefix and timestamp
  // options: { prefix: "LOG", includeTime: true }
}

// Test cases:
console.log('\nExercise 10:');
const log = createLogger({ prefix: 'APP' });
log('Starting...', 'Loading config');
// [APP] [timestamp] Starting...
// [APP] [timestamp] Loading config

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

/**
 * Exercise 11: Partial Application
 *
 * Create a partial function that pre-fills arguments.
 */
function partial(fn, ...presetArgs) {
  // TODO: Return function with preset args
}

// Test cases:
console.log('\nExercise 11:');
const add = (a, b, c) => a + b + c;
const addFive = partial(add, 5);
console.log(addFive(3, 2)); // 10

/**
 * Exercise 12: Validate Options
 *
 * Create a function that validates an options object
 * against a schema of required and optional fields.
 */
function validateOptions(options, schema) {
  // TODO: Validate options against schema
  // Return { valid: boolean, errors: string[] }
}

// Test cases:
console.log('\nExercise 12:');
const schema = {
  required: ['name', 'email'],
  optional: ['age', 'role'],
};
console.log(validateOptions({ name: 'Alice', email: 'a@b.com' }, schema));
// { valid: true, errors: [] }
console.log(validateOptions({ name: 'Alice' }, schema));
// { valid: false, errors: ["email is required"] }

/**
 * Exercise 13: Curry with Placeholder
 *
 * Create a curry function that supports placeholders.
 */
const _ = Symbol('placeholder');

function curry(fn) {
  // TODO: Implement curry with placeholder support
}

// Test cases:
console.log('\nExercise 13:');
// const divide = (a, b) => a / b;
// const curriedDivide = curry(divide);
// console.log(curriedDivide(10)(2));  // 5
// console.log(curriedDivide(_, 2)(10));  // 5 (10 / 2)

/**
 * Exercise 14: Options Merger
 *
 * Create a function that deeply merges options with defaults.
 */
function mergeWithDefaults(defaults, options) {
  // TODO: Deep merge options over defaults
}

// Test cases:
console.log('\nExercise 14:');
const defaults = {
  theme: 'light',
  settings: {
    notifications: true,
    sound: false,
  },
};
const userOptions = {
  settings: { sound: true },
};
console.log(mergeWithDefaults(defaults, userOptions));
// { theme: "light", settings: { notifications: true, sound: true } }

/**
 * Exercise 15: Function Overloading Pattern
 *
 * Create a function that behaves differently based on
 * the number and types of arguments.
 */
function createElement(tagOrConfig, attributes, children) {
  // TODO: Handle multiple calling patterns:
  // createElement("div")
  // createElement("div", { class: "box" })
  // createElement("div", { class: "box" }, ["child1", "child2"])
  // createElement({ tag: "div", class: "box", children: [...] })
}

// Test cases:
console.log('\nExercise 15:');
// console.log(createElement("div"));
// console.log(createElement("div", { class: "box" }));
// console.log(createElement({ tag: "span", class: "text" }));

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

/*
// Exercise 1 Solution:
function greet(name = "Guest", greeting = "Hello") {
    return `${greeting}, ${name}!`;
}

// Exercise 2 Solution:
function sumAll(...numbers) {
    return numbers.reduce((sum, n) => sum + n, 0);
}

// Exercise 3 Solution:
function createUser({ name, role = "user", active = true, createdAt = new Date() }) {
    return { name, role, active, createdAt };
}

// Exercise 4 Solution:
function getStats([first, ...rest]) {
    const all = [first, ...rest];
    return {
        first,
        last: rest.length > 0 ? rest[rest.length - 1] : first,
        count: all.length,
        sum: all.reduce((a, b) => a + b, 0)
    };
}

// Exercise 5 Solution:
function mergeObjects(...objects) {
    return objects.reduce((merged, obj) => ({ ...merged, ...obj }), {});
}

// Exercise 6 Solution:
function formatPrice({ amount, currency = "USD", locale = "en-US", decimals = 2 }) {
    return new Intl.NumberFormat(locale, {
        style: "currency",
        currency,
        minimumFractionDigits: decimals,
        maximumFractionDigits: decimals
    }).format(amount);
}

// Exercise 7 Solution:
function required(paramName) {
    throw new Error(`'${paramName}' is required`);
}

function registerUser(
    username = required("username"),
    email = required("email"),
    password = required("password")
) {
    return { username, email, password };
}

// Exercise 8 Solution:
function addProperties(obj, properties) {
    return { ...obj, ...properties };
}

// Exercise 9 Solution:
function extractUserInfo({ status, data: { user: { name, email } } }) {
    return { name, email, status };
}

// Exercise 10 Solution:
function createLogger({ prefix = "LOG", includeTime = true } = {}) {
    return (...messages) => {
        const time = includeTime ? `[${new Date().toISOString()}]` : "";
        messages.forEach(msg => {
            console.log(`[${prefix}] ${time} ${msg}`);
        });
    };
}

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

// Exercise 12 Solution:
function validateOptions(options, schema) {
    const errors = [];
    for (const field of schema.required) {
        if (!(field in options)) {
            errors.push(`${field} is required`);
        }
    }
    return { valid: errors.length === 0, errors };
}

// Exercise 13 Solution:
function curry(fn) {
    const arity = fn.length;
    const resolver = (...args) => {
        const hasPlaceholder = args.some(a => a === _);
        if (args.length >= arity && !hasPlaceholder) {
            return fn(...args);
        }
        return (...nextArgs) => {
            const combined = args.map(a => 
                a === _ && nextArgs.length ? nextArgs.shift() : a
            ).concat(nextArgs);
            return resolver(...combined);
        };
    };
    return resolver;
}

// Exercise 14 Solution:
function mergeWithDefaults(defaults, options) {
    const result = { ...defaults };
    for (const key in options) {
        if (typeof options[key] === "object" && options[key] !== null &&
            typeof result[key] === "object" && result[key] !== null) {
            result[key] = mergeWithDefaults(result[key], options[key]);
        } else {
            result[key] = options[key];
        }
    }
    return result;
}

// Exercise 15 Solution:
function createElement(tagOrConfig, attributes = {}, children = []) {
    if (typeof tagOrConfig === "object") {
        const { tag, children = [], ...attrs } = tagOrConfig;
        return { tag, attributes: attrs, children };
    }
    return { tag: tagOrConfig, attributes, children };
}
*/
Exercises - JavaScript Tutorial | DeepML