javascript

exercises

exercises.js
/**
 * =====================================================
 * 7.4 OBJECT STATIC METHODS - EXERCISES
 * =====================================================
 * Practice exercises for mastering Object static methods
 */

/**
 * EXERCISE 1: Object.keys(), values(), entries()
 * Difficulty: Easy
 *
 * Given an object, use Object methods to:
 * 1. Get all property names
 * 2. Get all property values
 * 3. Get all [key, value] pairs
 */
console.log('=== Exercise 1: keys, values, entries ===');

const book = {
  title: 'JavaScript Guide',
  author: 'John Doe',
  year: 2024,
  pages: 350,
};

// TODO: Use Object.keys, Object.values, and Object.entries

/* SOLUTION:
console.log("Keys:", Object.keys(book));
// ["title", "author", "year", "pages"]

console.log("Values:", Object.values(book));
// ["JavaScript Guide", "John Doe", 2024, 350]

console.log("Entries:", Object.entries(book));
// [["title", "JavaScript Guide"], ["author", "John Doe"], ["year", 2024], ["pages", 350]]
*/

/**
 * EXERCISE 2: Object.assign()
 * Difficulty: Easy
 *
 * Merge default settings with user settings.
 * User settings should override defaults.
 */
console.log('\n=== Exercise 2: Object.assign() ===');

const defaults = {
  theme: 'light',
  fontSize: 14,
  language: 'en',
  notifications: true,
};

const userSettings = {
  theme: 'dark',
  fontSize: 18,
};

// TODO: Merge settings so userSettings overrides defaults
let mergedSettings;

// console.log(mergedSettings);
// { theme: "dark", fontSize: 18, language: "en", notifications: true }

/* SOLUTION:
mergedSettings = Object.assign({}, defaults, userSettings);
console.log(mergedSettings);
*/

/**
 * EXERCISE 3: Object.fromEntries()
 * Difficulty: Easy
 *
 * Convert an array of [key, value] pairs into an object.
 */
console.log('\n=== Exercise 3: Object.fromEntries() ===');

const entries = [
  ['name', 'Alice'],
  ['age', 30],
  ['city', 'New York'],
  ['occupation', 'Developer'],
];

// TODO: Convert entries to an object

/* SOLUTION:
const person = Object.fromEntries(entries);
console.log(person);
// { name: "Alice", age: 30, city: "New York", occupation: "Developer" }
*/

/**
 * EXERCISE 4: Transform Object Values
 * Difficulty: Medium
 *
 * Use Object.entries and Object.fromEntries to double all values
 * in the prices object.
 */
console.log('\n=== Exercise 4: Transform Object Values ===');

const prices = {
  apple: 1.5,
  banana: 0.75,
  orange: 2.0,
  grape: 3.5,
};

// TODO: Double all prices using entries/fromEntries

/* SOLUTION:
const doubledPrices = Object.fromEntries(
    Object.entries(prices).map(([fruit, price]) => [fruit, price * 2])
);
console.log(doubledPrices);
// { apple: 3, banana: 1.5, orange: 4, grape: 7 }
*/

/**
 * EXERCISE 5: Filter Object Properties
 * Difficulty: Medium
 *
 * Create a function that filters an object to only include
 * properties that pass a predicate function.
 */
console.log('\n=== Exercise 5: Filter Object Properties ===');

// TODO: Implement filterObject function
function filterObject(obj, predicate) {
  // Your code here
}

// Test:
// const data = { a: 1, b: 2, c: 3, d: 4, e: 5 };
// const evens = filterObject(data, value => value % 2 === 0);
// console.log(evens);  // { b: 2, d: 4 }

/* SOLUTION:
function filterObject(obj, predicate) {
    return Object.fromEntries(
        Object.entries(obj).filter(([key, value]) => predicate(value, key))
    );
}

const data = { a: 1, b: 2, c: 3, d: 4, e: 5 };
const evens = filterObject(data, value => value % 2 === 0);
console.log(evens);  // { b: 2, d: 4 }
*/

/**
 * EXERCISE 6: Object.create()
 * Difficulty: Medium
 *
 * Create a new object that inherits from a prototype object.
 */
console.log('\n=== Exercise 6: Object.create() ===');

const vehicleProto = {
  start() {
    console.log(`Starting ${this.make} ${this.model}`);
  },
  stop() {
    console.log(`Stopping ${this.make} ${this.model}`);
  },
};

// TODO: Create a car object that inherits from vehicleProto
// Add properties: make = "Toyota", model = "Camry", year = 2024

/* SOLUTION:
const car = Object.create(vehicleProto);
car.make = "Toyota";
car.model = "Camry";
car.year = 2024;

car.start();  // "Starting Toyota Camry"
car.stop();   // "Stopping Toyota Camry"

// Alternative: Using Object.create with property descriptors
const car2 = Object.create(vehicleProto, {
    make: { value: "Toyota", enumerable: true },
    model: { value: "Camry", enumerable: true },
    year: { value: 2024, enumerable: true }
});
*/

/**
 * EXERCISE 7: Object.is()
 * Difficulty: Easy
 *
 * Use Object.is() to check for edge cases that === fails.
 */
console.log('\n=== Exercise 7: Object.is() ===');

// TODO: Check the following comparisons with both === and Object.is()
// 1. NaN with NaN
// 2. +0 with -0
// 3. null with undefined

/* SOLUTION:
console.log("NaN === NaN:", NaN === NaN);           // false
console.log("Object.is(NaN, NaN):", Object.is(NaN, NaN));  // true

console.log("+0 === -0:", +0 === -0);               // true
console.log("Object.is(+0, -0):", Object.is(+0, -0));      // false

console.log("null === undefined:", null === undefined);     // false
console.log("Object.is(null, undefined):", Object.is(null, undefined));  // false
*/

/**
 * EXERCISE 8: Object.hasOwn()
 * Difficulty: Easy
 *
 * Check if objects have their own properties (not inherited).
 */
console.log('\n=== Exercise 8: Object.hasOwn() ===');

const parent = { inherited: true };
const child = Object.create(parent);
child.own = 'my property';

// TODO: Check which properties are own vs inherited

/* SOLUTION:
console.log("child has 'own':", Object.hasOwn(child, "own"));           // true
console.log("child has 'inherited':", Object.hasOwn(child, "inherited")); // false
console.log("'inherited' in child:", "inherited" in child);             // true

// Works safely even with overridden hasOwnProperty
const dangerous = { hasOwnProperty: () => "hacked" };
console.log("Safe check:", Object.hasOwn(dangerous, "hasOwnProperty")); // true
*/

/**
 * EXERCISE 9: Object.freeze() vs seal()
 * Difficulty: Medium
 *
 * Demonstrate the difference between frozen and sealed objects.
 */
console.log('\n=== Exercise 9: freeze() vs seal() ===');

// TODO: Create frozen and sealed objects and test operations

/* SOLUTION:
const frozenObj = Object.freeze({ name: "Frozen", count: 0 });
const sealedObj = Object.seal({ name: "Sealed", count: 0 });

// Try to modify values
frozenObj.count = 100;
sealedObj.count = 100;
console.log("Frozen count:", frozenObj.count);  // 0 (unchanged)
console.log("Sealed count:", sealedObj.count);  // 100 (changed!)

// Try to add properties
frozenObj.newProp = "test";
sealedObj.newProp = "test";
console.log("Frozen has newProp:", "newProp" in frozenObj);  // false
console.log("Sealed has newProp:", "newProp" in sealedObj);  // false

// Try to delete properties
delete frozenObj.name;
delete sealedObj.name;
console.log("Frozen has name:", "name" in frozenObj);  // true
console.log("Sealed has name:", "name" in sealedObj);  // true
*/

/**
 * EXERCISE 10: Pick and Omit Functions
 * Difficulty: Medium
 *
 * Implement pick() and omit() functions using Object methods.
 */
console.log('\n=== Exercise 10: Pick and Omit ===');

// TODO: Implement pick and omit functions
function pick(obj, ...keys) {
  // Return object with only specified keys
}

function omit(obj, ...keys) {
  // Return object without specified keys
}

// Test:
// const user = { name: "Alice", age: 30, email: "alice@test.com", password: "secret" };
// console.log(pick(user, "name", "email"));   // { name: "Alice", email: "alice@test.com" }
// console.log(omit(user, "password"));        // { name: "Alice", age: 30, email: "alice@test.com" }

/* SOLUTION:
function pick(obj, ...keys) {
    return Object.fromEntries(
        Object.entries(obj).filter(([key]) => keys.includes(key))
    );
}

function omit(obj, ...keys) {
    return Object.fromEntries(
        Object.entries(obj).filter(([key]) => !keys.includes(key))
    );
}

const user = { name: "Alice", age: 30, email: "alice@test.com", password: "secret" };
console.log(pick(user, "name", "email"));
console.log(omit(user, "password"));
*/

/**
 * EXERCISE 11: Deep Clone
 * Difficulty: Hard
 *
 * Implement a deep clone function that preserves property descriptors.
 */
console.log('\n=== Exercise 11: Deep Clone ===');

// TODO: Implement deepClone function
function deepClone(obj) {
  // Your code here
}

// Test:
// const original = { a: 1, nested: { b: 2, deeper: { c: 3 } } };
// const cloned = deepClone(original);
// cloned.nested.b = 999;
// console.log(original.nested.b);  // Should still be 2

/* SOLUTION:
function deepClone(obj, seen = new WeakMap()) {
    // Handle primitives and null
    if (obj === null || typeof obj !== "object") {
        return obj;
    }
    
    // Handle circular references
    if (seen.has(obj)) {
        return seen.get(obj);
    }
    
    // Create clone with same prototype and descriptors
    const clone = Object.create(
        Object.getPrototypeOf(obj),
        Object.getOwnPropertyDescriptors(obj)
    );
    
    seen.set(obj, clone);
    
    // Recursively clone nested objects
    for (const key of Reflect.ownKeys(obj)) {
        const value = obj[key];
        if (value && typeof value === "object") {
            clone[key] = deepClone(value, seen);
        }
    }
    
    return clone;
}

const original = { a: 1, nested: { b: 2, deeper: { c: 3 } } };
const cloned = deepClone(original);
cloned.nested.b = 999;
console.log("Original:", original.nested.b);  // 2
console.log("Cloned:", cloned.nested.b);      // 999
*/

/**
 * EXERCISE 12: Invert Object
 * Difficulty: Medium
 *
 * Create a function that swaps keys and values in an object.
 */
console.log('\n=== Exercise 12: Invert Object ===');

// TODO: Implement invertObject function
function invertObject(obj) {
  // Your code here
}

// Test:
// const codes = { a: 1, b: 2, c: 3 };
// console.log(invertObject(codes));  // { 1: "a", 2: "b", 3: "c" }

/* SOLUTION:
function invertObject(obj) {
    return Object.fromEntries(
        Object.entries(obj).map(([key, value]) => [value, key])
    );
}

const codes = { a: 1, b: 2, c: 3 };
console.log(invertObject(codes));
*/

/**
 * EXERCISE 13: Merge Deep
 * Difficulty: Hard
 *
 * Implement a deep merge function that recursively merges objects.
 */
console.log('\n=== Exercise 13: Merge Deep ===');

// TODO: Implement mergeDeep function
function mergeDeep(target, ...sources) {
  // Your code here
}

// Test:
// const obj1 = { a: 1, b: { c: 2, d: 3 } };
// const obj2 = { b: { c: 10, e: 4 }, f: 5 };
// console.log(mergeDeep({}, obj1, obj2));
// // { a: 1, b: { c: 10, d: 3, e: 4 }, f: 5 }

/* SOLUTION:
function mergeDeep(target, ...sources) {
    if (!sources.length) return target;
    const source = sources.shift();
    
    if (isObject(target) && isObject(source)) {
        for (const key in source) {
            if (isObject(source[key])) {
                if (!target[key]) {
                    Object.assign(target, { [key]: {} });
                }
                mergeDeep(target[key], source[key]);
            } else {
                Object.assign(target, { [key]: source[key] });
            }
        }
    }
    
    return mergeDeep(target, ...sources);
}

function isObject(item) {
    return item && typeof item === "object" && !Array.isArray(item);
}

const obj1 = { a: 1, b: { c: 2, d: 3 } };
const obj2 = { b: { c: 10, e: 4 }, f: 5 };
console.log(mergeDeep({}, obj1, obj2));
*/

/**
 * EXERCISE 14: Object Diff
 * Difficulty: Hard
 *
 * Find the differences between two objects.
 */
console.log('\n=== Exercise 14: Object Diff ===');

// TODO: Implement objectDiff function
function objectDiff(obj1, obj2) {
  // Return an object showing what changed
}

// Test:
// const before = { name: "Alice", age: 30, city: "NYC" };
// const after = { name: "Alice", age: 31, country: "USA" };
// console.log(objectDiff(before, after));
// // { age: { from: 30, to: 31 }, city: { from: "NYC", to: undefined }, country: { from: undefined, to: "USA" } }

/* SOLUTION:
function objectDiff(obj1, obj2) {
    const allKeys = new Set([...Object.keys(obj1), ...Object.keys(obj2)]);
    const diff = {};
    
    for (const key of allKeys) {
        const val1 = obj1[key];
        const val2 = obj2[key];
        
        if (!Object.is(val1, val2)) {
            diff[key] = { from: val1, to: val2 };
        }
    }
    
    return diff;
}

const before = { name: "Alice", age: 30, city: "NYC" };
const after = { name: "Alice", age: 31, country: "USA" };
console.log(objectDiff(before, after));
*/

/**
 * EXERCISE 15: Group By
 * Difficulty: Medium
 *
 * Implement a groupBy function (similar to Object.groupBy).
 */
console.log('\n=== Exercise 15: Group By ===');

// TODO: Implement groupBy function
function groupBy(array, keyFn) {
  // Your code here
}

// Test:
// const people = [
//     { name: "Alice", department: "Engineering" },
//     { name: "Bob", department: "Marketing" },
//     { name: "Charlie", department: "Engineering" },
//     { name: "Diana", department: "HR" }
// ];
// console.log(groupBy(people, p => p.department));
// // { Engineering: [...], Marketing: [...], HR: [...] }

/* SOLUTION:
function groupBy(array, keyFn) {
    return array.reduce((groups, item) => {
        const key = keyFn(item);
        if (!groups[key]) {
            groups[key] = [];
        }
        groups[key].push(item);
        return groups;
    }, {});
}

const people = [
    { name: "Alice", department: "Engineering" },
    { name: "Bob", department: "Marketing" },
    { name: "Charlie", department: "Engineering" },
    { name: "Diana", department: "HR" }
];
console.log(groupBy(people, p => p.department));
*/

/**
 * EXERCISE 16: Map Object
 * Difficulty: Medium
 *
 * Create a mapObject function that applies a transformation
 * to each value (like Array.map for objects).
 */
console.log('\n=== Exercise 16: Map Object ===');

// TODO: Implement mapObject function
function mapObject(obj, transformFn) {
  // Your code here
}

// Test:
// const scores = { math: 80, english: 90, science: 85 };
// const curved = mapObject(scores, (value, key) => Math.min(100, value + 10));
// console.log(curved);  // { math: 90, english: 100, science: 95 }

/* SOLUTION:
function mapObject(obj, transformFn) {
    return Object.fromEntries(
        Object.entries(obj).map(([key, value]) => [key, transformFn(value, key)])
    );
}

const scores = { math: 80, english: 90, science: 85 };
const curved = mapObject(scores, (value, key) => Math.min(100, value + 10));
console.log(curved);
*/

/**
 * EXERCISE 17: Object to Query String
 * Difficulty: Medium
 *
 * Convert an object to a URL query string.
 */
console.log('\n=== Exercise 17: Object to Query String ===');

// TODO: Implement toQueryString function
function toQueryString(obj) {
  // Your code here
}

// Test:
// const params = { name: "John Doe", age: 30, city: "New York" };
// console.log(toQueryString(params));
// // "name=John%20Doe&age=30&city=New%20York"

/* SOLUTION:
function toQueryString(obj) {
    return Object.entries(obj)
        .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
        .join("&");
}

const params = { name: "John Doe", age: 30, city: "New York" };
console.log(toQueryString(params));
*/

/**
 * EXERCISE 18: Query String to Object
 * Difficulty: Medium
 *
 * Convert a URL query string back to an object.
 */
console.log('\n=== Exercise 18: Query String to Object ===');

// TODO: Implement fromQueryString function
function fromQueryString(queryString) {
  // Your code here
}

// Test:
// const qs = "name=John%20Doe&age=30&city=New%20York";
// console.log(fromQueryString(qs));
// // { name: "John Doe", age: "30", city: "New York" }

/* SOLUTION:
function fromQueryString(queryString) {
    if (!queryString) return {};
    
    return Object.fromEntries(
        queryString.split("&").map(pair => {
            const [key, value] = pair.split("=");
            return [decodeURIComponent(key), decodeURIComponent(value || "")];
        })
    );
}

const qs = "name=John%20Doe&age=30&city=New%20York";
console.log(fromQueryString(qs));
*/

/**
 * EXERCISE 19: Flatten Nested Object
 * Difficulty: Hard
 *
 * Flatten a nested object into a single-level object with dot notation keys.
 */
console.log('\n=== Exercise 19: Flatten Nested Object ===');

// TODO: Implement flattenObject function
function flattenObject(obj, prefix = '') {
  // Your code here
}

// Test:
// const nested = {
//     user: {
//         name: "Alice",
//         address: {
//             city: "NYC",
//             zip: "10001"
//         }
//     },
//     active: true
// };
// console.log(flattenObject(nested));
// // { "user.name": "Alice", "user.address.city": "NYC", "user.address.zip": "10001", "active": true }

/* SOLUTION:
function flattenObject(obj, prefix = "") {
    return Object.entries(obj).reduce((acc, [key, value]) => {
        const newKey = prefix ? `${prefix}.${key}` : key;
        
        if (value && typeof value === "object" && !Array.isArray(value)) {
            Object.assign(acc, flattenObject(value, newKey));
        } else {
            acc[newKey] = value;
        }
        
        return acc;
    }, {});
}

const nested = {
    user: {
        name: "Alice",
        address: {
            city: "NYC",
            zip: "10001"
        }
    },
    active: true
};
console.log(flattenObject(nested));
*/

/**
 * EXERCISE 20: Unflatten Object
 * Difficulty: Hard
 *
 * Reverse of flatten - convert dot notation keys back to nested object.
 */
console.log('\n=== Exercise 20: Unflatten Object ===');

// TODO: Implement unflattenObject function
function unflattenObject(obj) {
  // Your code here
}

// Test:
// const flat = {
//     "user.name": "Alice",
//     "user.address.city": "NYC",
//     "user.address.zip": "10001",
//     "active": true
// };
// console.log(unflattenObject(flat));
// // { user: { name: "Alice", address: { city: "NYC", zip: "10001" } }, active: true }

/* SOLUTION:
function unflattenObject(obj) {
    const result = {};
    
    for (const [key, value] of Object.entries(obj)) {
        const keys = key.split(".");
        let current = result;
        
        for (let i = 0; i < keys.length - 1; i++) {
            const k = keys[i];
            if (!(k in current)) {
                current[k] = {};
            }
            current = current[k];
        }
        
        current[keys[keys.length - 1]] = value;
    }
    
    return result;
}

const flat = {
    "user.name": "Alice",
    "user.address.city": "NYC",
    "user.address.zip": "10001",
    "active": true
};
console.log(unflattenObject(flat));
*/

console.log('\n=== All Exercises Complete ===');
console.log('Check the SOLUTION comments for answers!');
Exercises - JavaScript Tutorial | DeepML