javascript
exercises
exercises.js⚡javascript
/**
* ========================================
* 12.2 Key Array Methods - Exercises
* ========================================
*
* Practice using essential array methods.
*/
/**
* EXERCISE 1: Transform Product Data
*
* Use map() to transform an array of products into
* an array of formatted strings: "Name: $Price"
*/
function formatProducts(products) {
// YOUR CODE HERE:
// Return array of formatted strings
}
// const products = [
// { name: 'Apple', price: 1.5 },
// { name: 'Banana', price: 0.75 },
// { name: 'Cherry', price: 2.0 }
// ];
// console.log(formatProducts(products));
// ['Apple: $1.50', 'Banana: $0.75', 'Cherry: $2.00']
/*
* SOLUTION:
*
* function formatProducts(products) {
* return products.map(p => `${p.name}: $${p.price.toFixed(2)}`);
* }
*/
/**
* EXERCISE 2: Extract and Transform
*
* Given an array of users, extract emails and convert to lowercase.
*/
function extractEmails(users) {
// YOUR CODE HERE:
// Return array of lowercase emails
}
// const users = [
// { name: 'John', email: 'JOHN@Example.com' },
// { name: 'Jane', email: 'Jane@EXAMPLE.COM' },
// { name: 'Bob', email: 'Bob@example.COM' }
// ];
// console.log(extractEmails(users));
// ['john@example.com', 'jane@example.com', 'bob@example.com']
/*
* SOLUTION:
*
* function extractEmails(users) {
* return users.map(u => u.email.toLowerCase());
* }
*/
/**
* EXERCISE 3: Filter by Multiple Conditions
*
* Filter products that are:
* - In stock (quantity > 0)
* - In a specific category
* - Within a price range
*/
function filterProducts(products, category, minPrice, maxPrice) {
// YOUR CODE HERE:
// Return filtered products
}
// const products = [
// { name: 'Laptop', category: 'electronics', price: 999, quantity: 5 },
// { name: 'Phone', category: 'electronics', price: 699, quantity: 0 },
// { name: 'Tablet', category: 'electronics', price: 499, quantity: 10 },
// { name: 'Headphones', category: 'electronics', price: 199, quantity: 15 }
// ];
// console.log(filterProducts(products, 'electronics', 200, 700).map(p => p.name));
// ['Tablet']
/*
* SOLUTION:
*
* function filterProducts(products, category, minPrice, maxPrice) {
* return products.filter(p =>
* p.quantity > 0 &&
* p.category === category &&
* p.price >= minPrice &&
* p.price <= maxPrice
* );
* }
*/
/**
* EXERCISE 4: Calculate Order Total
*
* Use reduce() to calculate total cost of an order
* where each item has price and quantity.
*/
function calculateOrderTotal(items) {
// YOUR CODE HERE:
// Return total cost
}
// const order = [
// { name: 'Widget', price: 25, quantity: 4 },
// { name: 'Gadget', price: 50, quantity: 2 },
// { name: 'Gizmo', price: 15, quantity: 10 }
// ];
// console.log(calculateOrderTotal(order)); // 350
/*
* SOLUTION:
*
* function calculateOrderTotal(items) {
* return items.reduce((total, item) =>
* total + (item.price * item.quantity), 0
* );
* }
*/
/**
* EXERCISE 5: Count by Category
*
* Use reduce() to count items in each category.
*/
function countByCategory(items) {
// YOUR CODE HERE:
// Return { category: count, ... }
}
// const items = [
// { name: 'Apple', category: 'fruit' },
// { name: 'Carrot', category: 'vegetable' },
// { name: 'Banana', category: 'fruit' },
// { name: 'Broccoli', category: 'vegetable' },
// { name: 'Cherry', category: 'fruit' }
// ];
// console.log(countByCategory(items));
// { fruit: 3, vegetable: 2 }
/*
* SOLUTION:
*
* function countByCategory(items) {
* return items.reduce((counts, item) => {
* counts[item.category] = (counts[item.category] || 0) + 1;
* return counts;
* }, {});
* }
*/
/**
* EXERCISE 6: Find First and Last Match
*
* Use find() and findLast() to get first and last
* active user from an array.
*/
function findActiveUsers(users) {
// YOUR CODE HERE:
// Return { first, last }
}
// const users = [
// { id: 1, name: 'John', active: false },
// { id: 2, name: 'Jane', active: true },
// { id: 3, name: 'Bob', active: false },
// { id: 4, name: 'Alice', active: true },
// { id: 5, name: 'Charlie', active: true }
// ];
// console.log(findActiveUsers(users));
// { first: { id: 2, name: 'Jane', ... }, last: { id: 5, name: 'Charlie', ... } }
/*
* SOLUTION:
*
* function findActiveUsers(users) {
* return {
* first: users.find(u => u.active),
* last: users.findLast(u => u.active)
* };
* }
*/
/**
* EXERCISE 7: Validate Data
*
* Check if all items pass validation and if any have errors.
*/
function validateData(items) {
// YOUR CODE HERE:
// Return { allValid, hasErrors }
// Valid: has name (non-empty) and quantity >= 0
}
// const items = [
// { name: 'Widget', quantity: 5 },
// { name: 'Gadget', quantity: 0 },
// { name: '', quantity: 10 }
// ];
// console.log(validateData(items));
// { allValid: false, hasErrors: true }
/*
* SOLUTION:
*
* function validateData(items) {
* const isValid = item => item.name && item.name.trim() !== '' && item.quantity >= 0;
* return {
* allValid: items.every(isValid),
* hasErrors: items.some(item => !isValid(item))
* };
* }
*/
/**
* EXERCISE 8: Flatten Nested Data
*
* Extract all tags from posts that have nested tag arrays.
*/
function extractAllTags(posts) {
// YOUR CODE HERE:
// Return flat array of unique tags
}
// const posts = [
// { title: 'Post 1', tags: ['javascript', 'programming'] },
// { title: 'Post 2', tags: ['javascript', 'web'] },
// { title: 'Post 3', tags: ['programming', 'tutorial'] }
// ];
// console.log(extractAllTags(posts));
// ['javascript', 'programming', 'web', 'tutorial'] (unique)
/*
* SOLUTION:
*
* function extractAllTags(posts) {
* return [...new Set(posts.flatMap(post => post.tags))];
* }
*/
/**
* EXERCISE 9: Sort Complex Data
*
* Sort products by:
* 1. Category (alphabetically)
* 2. Price (ascending)
* 3. Name (alphabetically)
*/
function sortProducts(products) {
// YOUR CODE HERE:
// Return sorted copy
}
// const products = [
// { name: 'Widget A', category: 'tools', price: 30 },
// { name: 'Gadget B', category: 'electronics', price: 50 },
// { name: 'Widget B', category: 'tools', price: 30 },
// { name: 'Gadget A', category: 'electronics', price: 40 }
// ];
// console.log(sortProducts(products).map(p => p.name));
// ['Gadget A', 'Gadget B', 'Widget A', 'Widget B']
/*
* SOLUTION:
*
* function sortProducts(products) {
* return [...products].sort((a, b) => {
* if (a.category !== b.category) {
* return a.category.localeCompare(b.category);
* }
* if (a.price !== b.price) {
* return a.price - b.price;
* }
* return a.name.localeCompare(b.name);
* });
* }
*/
/**
* EXERCISE 10: Non-Mutating Update
*
* Use ES2023 methods (toSorted, with, toSpliced) to:
* - Update price at specific index
* - Sort by price
* - Remove item at index
* Without mutating original array.
*/
function processImmutably(products, indexToUpdate, newPrice, indexToRemove) {
// YOUR CODE HERE:
// Return { updated, sorted, removed }
}
// const products = [
// { name: 'A', price: 30 },
// { name: 'B', price: 20 },
// { name: 'C', price: 40 }
// ];
// const result = processImmutably(products, 1, 25, 2);
// console.log(result.updated); // Price at index 1 is now 25
// console.log(result.sorted); // Sorted by price
// console.log(result.removed); // Item at index 2 removed
/*
* SOLUTION:
*
* function processImmutably(products, indexToUpdate, newPrice, indexToRemove) {
* return {
* updated: products.with(indexToUpdate, {
* ...products[indexToUpdate],
* price: newPrice
* }),
* sorted: products.toSorted((a, b) => a.price - b.price),
* removed: products.toSpliced(indexToRemove, 1)
* };
* }
*/
/**
* EXERCISE 11: Chain Methods for Analysis
*
* Analyze orders:
* 1. Filter completed orders
* 2. Get orders over $100
* 3. Extract customer names
* 4. Remove duplicates
* 5. Sort alphabetically
*/
function analyzeOrders(orders) {
// YOUR CODE HERE:
// Return sorted, unique customer names of completed orders over $100
}
// const orders = [
// { id: 1, customer: 'John', total: 150, status: 'completed' },
// { id: 2, customer: 'Jane', total: 50, status: 'completed' },
// { id: 3, customer: 'John', total: 200, status: 'completed' },
// { id: 4, customer: 'Bob', total: 120, status: 'pending' },
// { id: 5, customer: 'Alice', total: 180, status: 'completed' }
// ];
// console.log(analyzeOrders(orders)); // ['Alice', 'John']
/*
* SOLUTION:
*
* function analyzeOrders(orders) {
* return [...new Set(
* orders
* .filter(o => o.status === 'completed')
* .filter(o => o.total > 100)
* .map(o => o.customer)
* )].sort();
* }
*/
/**
* EXERCISE 12: Reduce to Object
*
* Convert array of [key, value] pairs to object,
* handling duplicate keys by keeping the last value.
*/
function pairsToObject(pairs) {
// YOUR CODE HERE:
// Return object from pairs
}
// const pairs = [
// ['name', 'John'],
// ['age', 25],
// ['name', 'Jane'], // Duplicate - should override
// ['city', 'NYC']
// ];
// console.log(pairsToObject(pairs));
// { name: 'Jane', age: 25, city: 'NYC' }
/*
* SOLUTION:
*
* function pairsToObject(pairs) {
* return pairs.reduce((obj, [key, value]) => {
* obj[key] = value;
* return obj;
* }, {});
* }
*/
/**
* EXERCISE 13: Find Index with Complex Condition
*
* Find the index of the first product that:
* - Is in stock
* - Has price under specified max
* - Has name containing search term
*/
function findProductIndex(products, maxPrice, searchTerm) {
// YOUR CODE HERE:
// Return index or -1 if not found
}
// const products = [
// { name: 'Laptop Pro', price: 1500, inStock: true },
// { name: 'Laptop Basic', price: 800, inStock: false },
// { name: 'Laptop Mini', price: 600, inStock: true },
// { name: 'Desktop Pro', price: 900, inStock: true }
// ];
// console.log(findProductIndex(products, 1000, 'Laptop')); // 2
/*
* SOLUTION:
*
* function findProductIndex(products, maxPrice, searchTerm) {
* return products.findIndex(p =>
* p.inStock &&
* p.price < maxPrice &&
* p.name.toLowerCase().includes(searchTerm.toLowerCase())
* );
* }
*/
/**
* EXERCISE 14: Group and Sum
*
* Group sales by product and calculate total quantity and revenue.
*/
function summarizeSales(sales) {
// YOUR CODE HERE:
// Return { product: { quantity, revenue }, ... }
}
// const sales = [
// { product: 'Widget', quantity: 5, price: 10 },
// { product: 'Gadget', quantity: 3, price: 20 },
// { product: 'Widget', quantity: 2, price: 10 },
// { product: 'Gadget', quantity: 1, price: 20 }
// ];
// console.log(summarizeSales(sales));
// {
// Widget: { quantity: 7, revenue: 70 },
// Gadget: { quantity: 4, revenue: 80 }
// }
/*
* SOLUTION:
*
* function summarizeSales(sales) {
* return sales.reduce((summary, sale) => {
* if (!summary[sale.product]) {
* summary[sale.product] = { quantity: 0, revenue: 0 };
* }
* summary[sale.product].quantity += sale.quantity;
* summary[sale.product].revenue += sale.quantity * sale.price;
* return summary;
* }, {});
* }
*/
/**
* EXERCISE 15: Implement Custom flatMap
*
* Create your own flatMap function without using the built-in.
*/
function customFlatMap(arr, callback) {
// YOUR CODE HERE:
// Return flattened mapped array
}
// const nums = [1, 2, 3];
// const result = customFlatMap(nums, n => [n, n * 2]);
// console.log(result); // [1, 2, 2, 4, 3, 6]
/*
* SOLUTION:
*
* function customFlatMap(arr, callback) {
* return arr.reduce((result, item, index) => {
* return result.concat(callback(item, index, arr));
* }, []);
* }
*
* // Or using map and flat:
* function customFlatMap(arr, callback) {
* return arr.map(callback).reduce((a, b) => a.concat(b), []);
* }
*/
/**
* EXERCISE 16: Map with Index-Based Logic
*
* Create a function that applies different transformations
* based on element index:
* - Even indices: double the value
* - Odd indices: square the value
*/
function transformByIndex(numbers) {
// YOUR CODE HERE:
// Return transformed array
}
// console.log(transformByIndex([1, 2, 3, 4, 5]));
// [2, 4, 6, 16, 10] (indices: 0:*2, 1:^2, 2:*2, 3:^2, 4:*2)
/*
* SOLUTION:
*
* function transformByIndex(numbers) {
* return numbers.map((n, i) =>
* i % 2 === 0 ? n * 2 : n ** 2
* );
* }
*/
/**
* EXERCISE 17: Filter and Count
*
* Return both the filtered items and the count of filtered vs total.
*/
function filterWithStats(items, predicate) {
// YOUR CODE HERE:
// Return { filtered, matched, total, percentage }
}
// const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// const result = filterWithStats(numbers, n => n % 2 === 0);
// console.log(result);
// { filtered: [2, 4, 6, 8, 10], matched: 5, total: 10, percentage: 50 }
/*
* SOLUTION:
*
* function filterWithStats(items, predicate) {
* const filtered = items.filter(predicate);
* return {
* filtered,
* matched: filtered.length,
* total: items.length,
* percentage: (filtered.length / items.length) * 100
* };
* }
*/
/**
* EXERCISE 18: Reduce with Running Total
*
* Use reduce to create array of running totals (cumulative sum).
*/
function runningTotal(numbers) {
// YOUR CODE HERE:
// Return array of cumulative sums
}
// console.log(runningTotal([1, 2, 3, 4, 5]));
// [1, 3, 6, 10, 15]
/*
* SOLUTION:
*
* function runningTotal(numbers) {
* return numbers.reduce((result, num) => {
* const previousSum = result.length > 0 ? result[result.length - 1] : 0;
* result.push(previousSum + num);
* return result;
* }, []);
* }
*
* // Alternative using scan pattern:
* function runningTotal(numbers) {
* let sum = 0;
* return numbers.map(n => sum += n);
* }
*/
/**
* EXERCISE 19: Combine Every and Some
*
* Create a validation function that checks:
* - all required fields are present
* - at least one optional field is present
*/
function validateForm(data, required, optional) {
// YOUR CODE HERE:
// Return { allRequired, anyOptional, isValid }
}
// const data = { name: 'John', email: 'john@test.com', phone: '' };
// const result = validateForm(
// data,
// ['name', 'email'],
// ['phone', 'address', 'bio']
// );
// console.log(result);
// { allRequired: true, anyOptional: false, isValid: false }
/*
* SOLUTION:
*
* function validateForm(data, required, optional) {
* const hasValue = field => data[field] && data[field].trim() !== '';
* const allRequired = required.every(hasValue);
* const anyOptional = optional.some(hasValue);
* return {
* allRequired,
* anyOptional,
* isValid: allRequired && anyOptional
* };
* }
*/
/**
* EXERCISE 20: Chain Everything
*
* Process transaction data:
* 1. Filter only successful transactions
* 2. Group by type (income/expense)
* 3. Calculate total for each type
* 4. Return net balance
*/
function processTransactions(transactions) {
// YOUR CODE HERE:
// Return { income, expense, balance }
}
// const transactions = [
// { type: 'income', amount: 1000, status: 'success' },
// { type: 'expense', amount: 500, status: 'success' },
// { type: 'income', amount: 200, status: 'failed' },
// { type: 'expense', amount: 300, status: 'success' },
// { type: 'income', amount: 400, status: 'success' }
// ];
// console.log(processTransactions(transactions));
// { income: 1400, expense: 800, balance: 600 }
/*
* SOLUTION:
*
* function processTransactions(transactions) {
* const successful = transactions.filter(t => t.status === 'success');
*
* const income = successful
* .filter(t => t.type === 'income')
* .reduce((sum, t) => sum + t.amount, 0);
*
* const expense = successful
* .filter(t => t.type === 'expense')
* .reduce((sum, t) => sum + t.amount, 0);
*
* return { income, expense, balance: income - expense };
* }
*
* // Single reduce alternative:
* function processTransactions(transactions) {
* return transactions
* .filter(t => t.status === 'success')
* .reduce((result, t) => {
* result[t.type] += t.amount;
* result.balance += t.type === 'income' ? t.amount : -t.amount;
* return result;
* }, { income: 0, expense: 0, balance: 0 });
* }
*/
console.log('Key array methods exercises loaded!');