javascript

exercises

exercises.js
/**
 * ========================================
 * 12.1 Array Fundamentals - Exercises
 * ========================================
 *
 * Practice working with JavaScript arrays.
 */

/**
 * EXERCISE 1: Create Arrays Different Ways
 *
 * Create the same array [1, 2, 3, 4, 5] using:
 * - Array literal
 * - Array.of()
 * - Array.from() with length
 *
 * Verify all three produce the same result.
 */

function exercise1() {
  // YOUR CODE HERE:
  // const literal = ?
  // const arrayOf = ?
  // const arrayFrom = ?
  // console.log('Literal:', literal);
  // console.log('Array.of:', arrayOf);
  // console.log('Array.from:', arrayFrom);
}

// exercise1();

/*
 * SOLUTION:
 *
 * function exercise1() {
 *     const literal = [1, 2, 3, 4, 5];
 *     const arrayOf = Array.of(1, 2, 3, 4, 5);
 *     const arrayFrom = Array.from({ length: 5 }, (_, i) => i + 1);
 *
 *     console.log('Literal:', literal);
 *     console.log('Array.of:', arrayOf);
 *     console.log('Array.from:', arrayFrom);
 *
 *     // All should show [1, 2, 3, 4, 5]
 * }
 */

/**
 * EXERCISE 2: Access First, Middle, Last
 *
 * Given an array, return an object with:
 * - first: first element
 * - middle: middle element (if even length, choose lower middle)
 * - last: last element
 *
 * Use the .at() method for negative indexing.
 */

function getPositions(arr) {
  // YOUR CODE HERE:
  // Return { first, middle, last }
}

// console.log(getPositions([1, 2, 3, 4, 5]));  // { first: 1, middle: 3, last: 5 }
// console.log(getPositions(['a', 'b', 'c', 'd']));  // { first: 'a', middle: 'b', last: 'd' }

/*
 * SOLUTION:
 *
 * function getPositions(arr) {
 *     return {
 *         first: arr.at(0),
 *         middle: arr[Math.floor((arr.length - 1) / 2)],
 *         last: arr.at(-1)
 *     };
 * }
 */

/**
 * EXERCISE 3: Stack Operations
 *
 * Implement a simple stack using an array with:
 * - push(item): add to top
 * - pop(): remove and return from top
 * - peek(): view top without removing
 * - isEmpty(): check if empty
 * - size(): return number of items
 */

function createStack() {
  // YOUR CODE HERE:
  // Return object with push, pop, peek, isEmpty, size methods
}

// const stack = createStack();
// stack.push('a');
// stack.push('b');
// console.log(stack.peek());  // 'b'
// console.log(stack.pop());   // 'b'
// console.log(stack.size());  // 1

/*
 * SOLUTION:
 *
 * function createStack() {
 *     const items = [];
 *     return {
 *         push(item) { items.push(item); },
 *         pop() { return items.pop(); },
 *         peek() { return items.at(-1); },
 *         isEmpty() { return items.length === 0; },
 *         size() { return items.length; }
 *     };
 * }
 */

/**
 * EXERCISE 4: Queue Operations
 *
 * Implement a queue (FIFO) using an array with:
 * - enqueue(item): add to back
 * - dequeue(): remove and return from front
 * - front(): view front without removing
 * - isEmpty(): check if empty
 * - size(): return number of items
 */

function createQueue() {
  // YOUR CODE HERE:
  // Return object with enqueue, dequeue, front, isEmpty, size methods
}

// const queue = createQueue();
// queue.enqueue('first');
// queue.enqueue('second');
// console.log(queue.front());    // 'first'
// console.log(queue.dequeue());  // 'first'
// console.log(queue.size());     // 1

/*
 * SOLUTION:
 *
 * function createQueue() {
 *     const items = [];
 *     return {
 *         enqueue(item) { items.push(item); },
 *         dequeue() { return items.shift(); },
 *         front() { return items.at(0); },
 *         isEmpty() { return items.length === 0; },
 *         size() { return items.length; }
 *     };
 * }
 */

/**
 * EXERCISE 5: Insert At Index
 *
 * Write a function that inserts an element at a specific index
 * without mutating the original array.
 * If index is out of bounds, add to end.
 */

function insertAt(arr, index, element) {
  // YOUR CODE HERE:
  // Return new array with element inserted at index
}

// console.log(insertAt([1, 2, 4, 5], 2, 3));     // [1, 2, 3, 4, 5]
// console.log(insertAt([1, 2, 3], 0, 0));        // [0, 1, 2, 3]
// console.log(insertAt([1, 2, 3], 10, 4));       // [1, 2, 3, 4]

/*
 * SOLUTION:
 *
 * function insertAt(arr, index, element) {
 *     if (index >= arr.length) {
 *         return [...arr, element];
 *     }
 *     return [...arr.slice(0, index), element, ...arr.slice(index)];
 * }
 */

/**
 * EXERCISE 6: Remove At Index
 *
 * Write a function that removes an element at a specific index
 * without mutating the original array.
 * If index is out of bounds, return copy of original.
 */

function removeAt(arr, index) {
  // YOUR CODE HERE:
  // Return new array with element at index removed
}

// console.log(removeAt([1, 2, 3, 4, 5], 2));  // [1, 2, 4, 5]
// console.log(removeAt([1, 2, 3], 0));        // [2, 3]
// console.log(removeAt([1, 2, 3], 10));       // [1, 2, 3]

/*
 * SOLUTION:
 *
 * function removeAt(arr, index) {
 *     if (index < 0 || index >= arr.length) {
 *         return [...arr];
 *     }
 *     return [...arr.slice(0, index), ...arr.slice(index + 1)];
 * }
 */

/**
 * EXERCISE 7: Map - Double and Square
 *
 * Given an array of numbers:
 * - doubleAll: double each number
 * - squareAll: square each number
 * - doubleEvenSquareOdd: double evens, square odds
 */

function arrayOperations(numbers) {
  // YOUR CODE HERE:
  // Return { doubleAll, squareAll, doubleEvenSquareOdd }
}

// const nums = [1, 2, 3, 4, 5];
// const result = arrayOperations(nums);
// console.log(result.doubleAll);  // [2, 4, 6, 8, 10]
// console.log(result.squareAll);  // [1, 4, 9, 16, 25]
// console.log(result.doubleEvenSquareOdd);  // [1, 4, 9, 8, 25]

/*
 * SOLUTION:
 *
 * function arrayOperations(numbers) {
 *     return {
 *         doubleAll: numbers.map(n => n * 2),
 *         squareAll: numbers.map(n => n ** 2),
 *         doubleEvenSquareOdd: numbers.map(n => n % 2 === 0 ? n * 2 : n ** 2)
 *     };
 * }
 */

/**
 * EXERCISE 8: Filter Complex Conditions
 *
 * Given an array of products, create functions to filter:
 * - inStock: products where inStock is true
 * - affordable: products with price < 50
 * - available: products that are inStock AND price < 100
 */

function filterProducts(products) {
  // YOUR CODE HERE:
  // Return { inStock, affordable, available }
}

// const products = [
//     { name: 'Phone', price: 699, inStock: true },
//     { name: 'Cable', price: 19, inStock: true },
//     { name: 'Case', price: 29, inStock: false },
//     { name: 'Earbuds', price: 89, inStock: true }
// ];
// const filtered = filterProducts(products);
// console.log(filtered.inStock.map(p => p.name));    // ['Phone', 'Cable', 'Earbuds']
// console.log(filtered.affordable.map(p => p.name)); // ['Cable', 'Case']
// console.log(filtered.available.map(p => p.name)); // ['Cable', 'Earbuds']

/*
 * SOLUTION:
 *
 * function filterProducts(products) {
 *     return {
 *         inStock: products.filter(p => p.inStock),
 *         affordable: products.filter(p => p.price < 50),
 *         available: products.filter(p => p.inStock && p.price < 100)
 *     };
 * }
 */

/**
 * EXERCISE 9: Reduce - Statistics
 *
 * Calculate statistics for an array of numbers:
 * - sum: total of all numbers
 * - average: mean value
 * - min: smallest number
 * - max: largest number
 */

function calculateStats(numbers) {
  // YOUR CODE HERE:
  // Return { sum, average, min, max }
}

// console.log(calculateStats([10, 20, 30, 40, 50]));
// { sum: 150, average: 30, min: 10, max: 50 }

/*
 * SOLUTION:
 *
 * function calculateStats(numbers) {
 *     if (numbers.length === 0) return { sum: 0, average: 0, min: undefined, max: undefined };
 *
 *     const sum = numbers.reduce((acc, n) => acc + n, 0);
 *     return {
 *         sum,
 *         average: sum / numbers.length,
 *         min: numbers.reduce((a, b) => Math.min(a, b)),
 *         max: numbers.reduce((a, b) => Math.max(a, b))
 *     };
 * }
 */

/**
 * EXERCISE 10: Reduce - Group By
 *
 * Group an array of objects by a specific property.
 */

function groupBy(array, property) {
  // YOUR CODE HERE:
  // Return object with keys as property values, values as arrays
}

// const people = [
//     { name: 'John', department: 'Sales' },
//     { name: 'Jane', department: 'Engineering' },
//     { name: 'Bob', department: 'Sales' },
//     { name: 'Alice', department: 'Engineering' }
// ];
// console.log(groupBy(people, 'department'));
// {
//     Sales: [{ name: 'John', ... }, { name: 'Bob', ... }],
//     Engineering: [{ name: 'Jane', ... }, { name: 'Alice', ... }]
// }

/*
 * SOLUTION:
 *
 * function groupBy(array, property) {
 *     return array.reduce((acc, item) => {
 *         const key = item[property];
 *         (acc[key] = acc[key] || []).push(item);
 *         return acc;
 *     }, {});
 * }
 */

/**
 * EXERCISE 11: Find and Validate
 *
 * Given an array of users:
 * - findByEmail: find user by email (case-insensitive)
 * - hasAdmin: check if any user has role 'admin'
 * - allVerified: check if all users are verified
 */

function userOperations(users) {
  // YOUR CODE HERE:
  // Return { findByEmail, hasAdmin, allVerified }
}

// const users = [
//     { name: 'John', email: 'John@test.com', role: 'user', verified: true },
//     { name: 'Jane', email: 'jane@test.com', role: 'admin', verified: true },
//     { name: 'Bob', email: 'bob@test.com', role: 'user', verified: false }
// ];
// const ops = userOperations(users);
// console.log(ops.findByEmail('JOHN@TEST.COM'));  // { name: 'John', ... }
// console.log(ops.hasAdmin);    // true
// console.log(ops.allVerified); // false

/*
 * SOLUTION:
 *
 * function userOperations(users) {
 *     return {
 *         findByEmail(email) {
 *             return users.find(u =>
 *                 u.email.toLowerCase() === email.toLowerCase()
 *             );
 *         },
 *         hasAdmin: users.some(u => u.role === 'admin'),
 *         allVerified: users.every(u => u.verified)
 *     };
 * }
 */

/**
 * EXERCISE 12: Array Transformation Pipeline
 *
 * Create a function that processes orders:
 * 1. Filter only completed orders
 * 2. Extract total amounts
 * 3. Calculate grand total and average
 */

function processOrders(orders) {
  // YOUR CODE HERE:
  // Return { completedCount, grandTotal, averageOrder }
}

// const orders = [
//     { id: 1, status: 'completed', total: 100 },
//     { id: 2, status: 'pending', total: 50 },
//     { id: 3, status: 'completed', total: 200 },
//     { id: 4, status: 'cancelled', total: 75 },
//     { id: 5, status: 'completed', total: 150 }
// ];
// console.log(processOrders(orders));
// { completedCount: 3, grandTotal: 450, averageOrder: 150 }

/*
 * SOLUTION:
 *
 * function processOrders(orders) {
 *     const completed = orders.filter(o => o.status === 'completed');
 *     const totals = completed.map(o => o.total);
 *     const grandTotal = totals.reduce((sum, t) => sum + t, 0);
 *
 *     return {
 *         completedCount: completed.length,
 *         grandTotal,
 *         averageOrder: completed.length > 0 ? grandTotal / completed.length : 0
 *     };
 * }
 */

/**
 * EXERCISE 13: Flatten and Extract
 *
 * Given nested data, flatten and extract specific values:
 * - flattenCategories: flatten all product names
 * - getTotalProducts: count all products
 * - getCategoriesWithProducts: categories with product count
 */

function analyzeCategories(categories) {
  // YOUR CODE HERE:
  // Return { allProductNames, totalProducts, categoriesWithCounts }
}

// const categories = [
//     { name: 'Electronics', products: ['Phone', 'Laptop', 'Tablet'] },
//     { name: 'Clothing', products: ['Shirt', 'Pants'] },
//     { name: 'Books', products: ['Novel', 'Textbook', 'Magazine'] }
// ];
// const analysis = analyzeCategories(categories);
// console.log(analysis.allProductNames);
// ['Phone', 'Laptop', 'Tablet', 'Shirt', 'Pants', 'Novel', 'Textbook', 'Magazine']
// console.log(analysis.totalProducts);  // 8
// console.log(analysis.categoriesWithCounts);
// [{ name: 'Electronics', count: 3 }, ...]

/*
 * SOLUTION:
 *
 * function analyzeCategories(categories) {
 *     return {
 *         allProductNames: categories.flatMap(c => c.products),
 *         totalProducts: categories.reduce((sum, c) => sum + c.products.length, 0),
 *         categoriesWithCounts: categories.map(c => ({
 *             name: c.name,
 *             count: c.products.length
 *         }))
 *     };
 * }
 */

/**
 * EXERCISE 14: Sort Complex Data
 *
 * Sort an array of students by multiple criteria:
 * - byGrade: descending by grade
 * - byName: alphabetically by name
 * - byGradeThenName: by grade desc, then name asc
 */

function sortStudents(students) {
  // YOUR CODE HERE:
  // Return { byGrade, byName, byGradeThenName }
}

// const students = [
//     { name: 'Charlie', grade: 85 },
//     { name: 'Alice', grade: 92 },
//     { name: 'Bob', grade: 85 },
//     { name: 'Diana', grade: 78 }
// ];
// const sorted = sortStudents(students);
// console.log(sorted.byGrade.map(s => s.name));
// ['Alice', 'Charlie', 'Bob', 'Diana']
// console.log(sorted.byGradeThenName.map(s => s.name));
// ['Alice', 'Bob', 'Charlie', 'Diana']

/*
 * SOLUTION:
 *
 * function sortStudents(students) {
 *     return {
 *         byGrade: [...students].sort((a, b) => b.grade - a.grade),
 *         byName: [...students].sort((a, b) => a.name.localeCompare(b.name)),
 *         byGradeThenName: [...students].sort((a, b) => {
 *             if (b.grade !== a.grade) return b.grade - a.grade;
 *             return a.name.localeCompare(b.name);
 *         })
 *     };
 * }
 */

/**
 * EXERCISE 15: Remove Duplicates
 *
 * Create functions to remove duplicates:
 * - removeDuplicates: from array of primitives
 * - removeDuplicatesByKey: from array of objects using key
 */

function removeDuplicates(arr) {
  // YOUR CODE HERE:
  // Return array with duplicates removed
}

function removeDuplicatesByKey(arr, key) {
  // YOUR CODE HERE:
  // Return array with duplicates by key removed (keep first)
}

// console.log(removeDuplicates([1, 2, 2, 3, 3, 3, 4]));  // [1, 2, 3, 4]
// console.log(removeDuplicates(['a', 'b', 'a', 'c', 'b']));  // ['a', 'b', 'c']

// const items = [
//     { id: 1, name: 'Apple' },
//     { id: 2, name: 'Banana' },
//     { id: 1, name: 'Apple Updated' },
//     { id: 3, name: 'Cherry' }
// ];
// console.log(removeDuplicatesByKey(items, 'id'));
// [{ id: 1, name: 'Apple' }, { id: 2, name: 'Banana' }, { id: 3, name: 'Cherry' }]

/*
 * SOLUTION:
 *
 * function removeDuplicates(arr) {
 *     return [...new Set(arr)];
 * }
 *
 * function removeDuplicatesByKey(arr, key) {
 *     const seen = new Set();
 *     return arr.filter(item => {
 *         if (seen.has(item[key])) return false;
 *         seen.add(item[key]);
 *         return true;
 *     });
 * }
 */

/**
 * EXERCISE 16: Array Chunking
 *
 * Split an array into chunks of specified size.
 */

function chunkArray(arr, size) {
  // YOUR CODE HERE:
  // Return array of arrays, each with 'size' elements
}

// console.log(chunkArray([1, 2, 3, 4, 5, 6, 7], 3));
// [[1, 2, 3], [4, 5, 6], [7]]
// console.log(chunkArray([1, 2, 3, 4], 2));
// [[1, 2], [3, 4]]

/*
 * SOLUTION:
 *
 * function chunkArray(arr, size) {
 *     return Array.from(
 *         { length: Math.ceil(arr.length / size) },
 *         (_, i) => arr.slice(i * size, i * size + size)
 *     );
 * }
 *
 * // Alternative with reduce:
 * function chunkArray(arr, size) {
 *     return arr.reduce((chunks, item, i) => {
 *         if (i % size === 0) chunks.push([]);
 *         chunks[chunks.length - 1].push(item);
 *         return chunks;
 *     }, []);
 * }
 */

/**
 * EXERCISE 17: Array Intersection and Difference
 *
 * Create functions for set-like operations:
 * - intersection: elements in both arrays
 * - difference: elements in first but not second
 * - union: elements in either array (no duplicates)
 */

function setOperations(arr1, arr2) {
  // YOUR CODE HERE:
  // Return { intersection, difference, union }
}

// console.log(setOperations([1, 2, 3, 4], [3, 4, 5, 6]));
// {
//     intersection: [3, 4],
//     difference: [1, 2],
//     union: [1, 2, 3, 4, 5, 6]
// }

/*
 * SOLUTION:
 *
 * function setOperations(arr1, arr2) {
 *     const set1 = new Set(arr1);
 *     const set2 = new Set(arr2);
 *
 *     return {
 *         intersection: arr1.filter(x => set2.has(x)),
 *         difference: arr1.filter(x => !set2.has(x)),
 *         union: [...new Set([...arr1, ...arr2])]
 *     };
 * }
 */

/**
 * EXERCISE 18: Zip Arrays
 *
 * Combine multiple arrays element by element.
 */

function zip(...arrays) {
  // YOUR CODE HERE:
  // Return array of arrays, each containing elements at same index
}

// console.log(zip([1, 2, 3], ['a', 'b', 'c']));
// [[1, 'a'], [2, 'b'], [3, 'c']]
// console.log(zip([1, 2], ['a', 'b'], ['x', 'y']));
// [[1, 'a', 'x'], [2, 'b', 'y']]

/*
 * SOLUTION:
 *
 * function zip(...arrays) {
 *     const minLength = Math.min(...arrays.map(a => a.length));
 *     return Array.from({ length: minLength }, (_, i) =>
 *         arrays.map(arr => arr[i])
 *     );
 * }
 */

/**
 * EXERCISE 19: Rotate Array
 *
 * Rotate array elements by n positions:
 * - Positive n: rotate right
 * - Negative n: rotate left
 */

function rotateArray(arr, n) {
  // YOUR CODE HERE:
  // Return new rotated array
}

// console.log(rotateArray([1, 2, 3, 4, 5], 2));   // [4, 5, 1, 2, 3]
// console.log(rotateArray([1, 2, 3, 4, 5], -2));  // [3, 4, 5, 1, 2]
// console.log(rotateArray([1, 2, 3], 0));         // [1, 2, 3]

/*
 * SOLUTION:
 *
 * function rotateArray(arr, n) {
 *     const len = arr.length;
 *     if (len === 0) return [...arr];
 *
 *     // Normalize n to be within array bounds
 *     const rotation = ((n % len) + len) % len;
 *
 *     return [...arr.slice(-rotation), ...arr.slice(0, -rotation || len)];
 * }
 */

/**
 * EXERCISE 20: Deep Flatten
 *
 * Flatten an array of any nesting depth.
 * Do NOT use Array.flat().
 */

function deepFlatten(arr) {
  // YOUR CODE HERE:
  // Return completely flattened array
}

// console.log(deepFlatten([1, [2, [3, [4, [5]]]]]));  // [1, 2, 3, 4, 5]
// console.log(deepFlatten([[[[1]]], [[[2]]]]));      // [1, 2]

/*
 * SOLUTION:
 *
 * function deepFlatten(arr) {
 *     return arr.reduce((flat, item) =>
 *         flat.concat(Array.isArray(item) ? deepFlatten(item) : item),
 *         []
 *     );
 * }
 *
 * // Alternative with recursion:
 * function deepFlatten(arr) {
 *     const result = [];
 *     for (const item of arr) {
 *         if (Array.isArray(item)) {
 *             result.push(...deepFlatten(item));
 *         } else {
 *             result.push(item);
 *         }
 *     }
 *     return result;
 * }
 */

/**
 * BONUS EXERCISE 21: Partition
 *
 * Split array into two based on predicate:
 * - First array: elements that pass
 * - Second array: elements that fail
 */

function partition(arr, predicate) {
  // YOUR CODE HERE:
  // Return [passing, failing]
}

// const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// console.log(partition(nums, n => n % 2 === 0));
// [[2, 4, 6, 8, 10], [1, 3, 5, 7, 9]]

/*
 * SOLUTION:
 *
 * function partition(arr, predicate) {
 *     return arr.reduce(
 *         ([pass, fail], item) =>
 *             predicate(item) ? [[...pass, item], fail] : [pass, [...fail, item]],
 *         [[], []]
 *     );
 * }
 *
 * // Alternative clearer version:
 * function partition(arr, predicate) {
 *     const pass = [];
 *     const fail = [];
 *     for (const item of arr) {
 *         (predicate(item) ? pass : fail).push(item);
 *     }
 *     return [pass, fail];
 * }
 */

/**
 * BONUS EXERCISE 22: Frequency Counter
 *
 * Count frequency of each unique element.
 * Return sorted by frequency (descending).
 */

function frequencyCounter(arr) {
  // YOUR CODE HERE:
  // Return array of [element, count] pairs sorted by count
}

// console.log(frequencyCounter(['a', 'b', 'a', 'c', 'a', 'b']));
// [['a', 3], ['b', 2], ['c', 1]]

/*
 * SOLUTION:
 *
 * function frequencyCounter(arr) {
 *     const counts = arr.reduce((acc, item) => {
 *         acc[item] = (acc[item] || 0) + 1;
 *         return acc;
 *     }, {});
 *
 *     return Object.entries(counts)
 *         .sort((a, b) => b[1] - a[1]);
 * }
 */

console.log('Array exercises loaded! Uncomment tests to practice.');
Exercises - JavaScript Tutorial | DeepML