Practice Questions

1. What is the difference between imperative and declarative programming? Provide a simple example showing both approaches to double each number in an array.

Solution

Imperative programming describes how to achieve a result with step-by-step instructions. Declarative programming describes what result you want, letting the language handle the details.

const numbers = [1, 2, 3, 4, 5];

// Imperative: explicitly manage the loop and new array
const doubled1 = [];
for (let i = 0; i < numbers.length; i++) {
  doubled1.push(numbers[i] * 2);
}

// Declarative: describe the transformation
const doubled2 = numbers.map((n) => n * 2);

console.log(doubled1);  // [2, 4, 6, 8, 10]
console.log(doubled2);  // [2, 4, 6, 8, 10]

The declarative version reads like a description: “map each number to its double.” You do not manage indices or push operations. map handles those details.

2. Given an array of user objects, write code using map to extract just the email addresses into a new array.

const users = [
  { id: 1, name: "Alice", email: "alice@example.com" },
  { id: 2, name: "Bob", email: "bob@example.com" },
  { id: 3, name: "Charlie", email: "charlie@example.com" },
];
Solution
const users = [
  { id: 1, name: "Alice", email: "alice@example.com" },
  { id: 2, name: "Bob", email: "bob@example.com" },
  { id: 3, name: "Charlie", email: "charlie@example.com" },
];

const emails = users.map((user) => user.email);
console.log(emails);
// ["alice@example.com", "bob@example.com", "charlie@example.com"]

map creates a new array by applying the callback to each element. The original array remains unchanged.

3. Given an array of products, write code using filter to get only the products that are in stock AND cost less than $500.

const products = [
  { name: "Laptop", price: 999, inStock: true },
  { name: "Phone", price: 699, inStock: false },
  { name: "Tablet", price: 449, inStock: true },
  { name: "Watch", price: 299, inStock: true },
];
Solution
const products = [
  { name: "Laptop", price: 999, inStock: true },
  { name: "Phone", price: 699, inStock: false },
  { name: "Tablet", price: 449, inStock: true },
  { name: "Watch", price: 299, inStock: true },
];

const affordable = products.filter((p) => p.inStock && p.price < 500);
console.log(affordable);
// [
//   { name: "Tablet", price: 449, inStock: true },
//   { name: "Watch", price: 299, inStock: true }
// ]

Use && to combine multiple conditions in the filter callback.

4. Write code using reduce to calculate the total price of all items in a shopping cart.

const cart = [
  { name: "Book", price: 15 },
  { name: "Pen", price: 2 },
  { name: "Notebook", price: 8 },
];
Solution
const cart = [
  { name: "Book", price: 15 },
  { name: "Pen", price: 2 },
  { name: "Notebook", price: 8 },
];

const total = cart.reduce((sum, item) => sum + item.price, 0);
console.log(total);  // 25

The reduce callback receives the accumulator (sum) and current element (item). The second argument (0) is the initial value. Always provide one to avoid errors with empty arrays.

5. What is the difference between find and filter? When would you use each?

Solution
  • find returns the first element that matches the condition (or undefined if none match)
  • filter returns a new array containing all elements that match the condition
const numbers = [1, 5, 10, 15, 20];

const firstOver10 = numbers.find((n) => n > 10);
console.log(firstOver10);  // 15 (just the first match)

const allOver10 = numbers.filter((n) => n > 10);
console.log(allOver10);  // [15, 20] (all matches)

Use find when you need a single element (e.g., looking up a user by ID). Use filter when you need all matching elements (e.g., getting all active users).

6. Write code that chains filter and map to get the names of all users who are older than 25.

const users = [
  { name: "Alice", age: 28 },
  { name: "Bob", age: 22 },
  { name: "Charlie", age: 35 },
  { name: "Diana", age: 19 },
];
Solution
const users = [
  { name: "Alice", age: 28 },
  { name: "Bob", age: 22 },
  { name: "Charlie", age: 35 },
  { name: "Diana", age: 19 },
];

const names = users.filter((user) => user.age > 25).map((user) => user.name);

console.log(names);  // ["Alice", "Charlie"]

You can read the chain as a sentence: “filter to users over 25, then map to their names.” Chaining is common in declarative programming. Each step describes what happens to the data.

7. What is the difference between some and every? Write code that checks (a) if any product is out of stock, and (b) if all products are under $1000.

const products = [
  { name: "Laptop", price: 999, inStock: true },
  { name: "Phone", price: 699, inStock: false },
  { name: "Tablet", price: 449, inStock: true },
];
Solution
  • some returns true if at least one element passes the test
  • every returns true only if all elements pass the test
const products = [
  { name: "Laptop", price: 999, inStock: true },
  { name: "Phone", price: 699, inStock: false },
  { name: "Tablet", price: 449, inStock: true },
];

// (a) Is any product out of stock?
const hasOutOfStock = products.some((p) => !p.inStock);
console.log(hasOutOfStock);  // true

// (b) Are all products under $1000?
const allAffordable = products.every((p) => p.price < 1000);
console.log(allAffordable);  // true

Both methods short-circuit: some stops when it finds a match, every stops when it finds a failure.

8. The sort method has a gotcha with numbers. What is the problem, and how do you fix it? Show the correct way to sort an array of numbers in ascending order.

Solution

The problem is that sort converts elements to strings by default, so numbers end up in the wrong order:

const numbers = [10, 2, 5, 1, 9];
numbers.sort();
console.log(numbers);  // [1, 10, 2, 5, 9] - Wrong! (sorted as strings)

To fix it, provide a compare function:

const numbers = [10, 2, 5, 1, 9];
const ascending = [...numbers].sort((a, b) => a - b);  // Ascending
console.log(ascending);  // [1, 2, 5, 9, 10] - Correct!

// For descending order:
const descending = [...numbers].sort((a, b) => b - a);
console.log(descending);  // [10, 9, 5, 2, 1]

The compare function should return a negative number if a should come before b, positive if b should come before a, and zero if they are equal.

Note: sort mutates the array it is called on, so copying first (like [...numbers]) is a common pattern when you want to keep the original array unchanged.

9. Write code using reduce to count how many times each fruit appears in the array.

const fruits = ["apple", "banana", "apple", "orange", "banana", "apple"];
// Expected: { apple: 3, banana: 2, orange: 1 }
Solution
const fruits = ["apple", "banana", "apple", "orange", "banana", "apple"];

const counts = fruits.reduce((acc, fruit) => {
  acc[fruit] = (acc[fruit] || 0) + 1;
  return acc;
}, {});

console.log(counts);  // { apple: 3, banana: 2, orange: 1 }

The initial value is an empty object {}. For each fruit, we either increment its existing count or initialize it to 1. The expression (acc[fruit] || 0) + 1 handles both cases: if acc[fruit] is undefined, it defaults to 0, then adds 1.

10. Given two Sets, write code to find their intersection (elements that exist in both sets).

const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);
// Expected intersection: [3, 4]
Solution
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);

const intersection = new Set([...setA].filter((x) => setB.has(x)));
console.log([...intersection]);  // [3, 4]

The pattern is:

  1. Spread one set into an array: [...setA]
  2. Filter to keep only elements that the other set has: .filter(x => setB.has(x))
  3. Wrap in a new Set if you need a Set result

For union (all elements from both), use: new Set([...setA, ...setB])

For difference (elements in A but not B), use: new Set([...setA].filter(x => !setB.has(x)))