Lesson 10: Array Methods - map, filter, reduce

What You'll Learn

  • Higher-order array methods
  • map() - transform each element
  • filter() - select elements
  • reduce() - combine elements
  • find() and findIndex()
  • some() and every()
  • Method chaining

Why This Matters

Array methods are powerful tools that let you process data without writing explicit loops. They make your code shorter, clearer, and more functional. These methods are used everywhere in modern JavaScript development.

---

Part 1: What are Higher-Order Functions?

A higher-order function is a function that:

  • Takes another function as an argument, OR
  • Returns a function

Array methods like map, filter, and reduce take functions as arguments.

Create a new file: lesson-10/array-methods.js

---

Part 2: The map() Method

map() creates a new array by transforming each element.

Basic Example

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

// Transform each number by doubling it
const doubled = numbers.map((num) => num * 2);

console.log(doubled);  // [2, 4, 6, 8, 10]
console.log(numbers);  // [1, 2, 3, 4, 5] - original unchanged

How map() Works

// What map does (simplified):
function map(array, transformFunction) {
  const result = [];
  for (const item of array) {
    result.push(transformFunction(item));
  }
  return result;
}

The Callback Function

const numbers = [1, 2, 3];

// Arrow function (concise)
const doubled = numbers.map(num => num * 2);

// Full arrow function
const doubled2 = numbers.map((num) => {
  return num * 2;
});

// Traditional function
const doubled3 = numbers.map(function(num) {
  return num * 2;
});

map() with Index and Array

const numbers = [10, 20, 30];

// Callback gets: (element, index, wholeArray)
const result = numbers.map((num, index, array) => {
  console.log(`Index ${index}: ${num} (Array length: ${array.length})`);
  return num * index;
});

console.log(result);  // [0, 20, 60]

Practical Examples

// Convert strings to uppercase
const names = ["alice", "bob", "charlie"];
const uppercased = names.map(name => name.toUpperCase());
console.log(uppercased);  // ["ALICE", "BOB", "CHARLIE"]

// Extract property from objects

const products[] = [
  { name: "Laptop", price: 999 },
  { name: "Mouse", price: 25 },
  { name: "Keyboard", price: 75 }
];

const prices = products.map(product => product.price);
console.log(prices);  // [999, 25, 75]

// Add tax to prices
const pricesWithTax = prices.map(price => price * 1.1);
console.log(pricesWithTax);  // [1098.9, 27.5, 82.5]

---

Part 3: The filter() Method

filter() creates a new array with only elements that pass a test.

Basic Example

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Keep only even numbers
const evens = numbers.filter(num => num % 2 === 0);

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

How filter() Works

// What filter does (simplified):
function filter(array, testFunction) {
  const result = [];
  for (const item of array) {
    if (testFunction(item)) {  // If test passes
      result.push(item);        // Include in result
    }
  }
  return result;
}

Practical Examples

// Filter numbers greater than 5
const numbers = [3, 8, 1, 9, 2, 7];
const bigNumbers = numbers.filter(num => num > 5);
console.log(bigNumbers);  // [8, 9, 7]

// Filter strings by length
const words = ["cat", "elephant", "dog", "butterfly"];
const longWords = words.filter(word => word.length > 5);
console.log(longWords);  // ["elephant", "butterfly"]

// Filter objects by condition

const users[] = [
  { name: "Alice", age: 30, isActive: true },
  { name: "Bob", age: 17, isActive: false },
  { name: "Charlie", age: 25, isActive: true }
];

// Get only active adult users
const activeAdults[] = users.filter(user => 
  user.age >= 18 && user.isActive
);

console.log(activeAdults);
// [{ name: "Alice", age: 30, isActive: true },
//  { name: "Charlie", age: 25, isActive: true }]

---

Part 4: The reduce() Method

reduce() reduces an array to a single value by combining elements.

Basic Example - Sum

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

// Add all numbers together
const sum = numbers.reduce((accumulator, current) => {
  return accumulator + current;
}, 0);  // 0 is the initial value

console.log(sum);  // 15

Understanding reduce()

// How reduce works step-by-step:
const numbers = [1, 2, 3, 4, 5];

// Initial value: 0
// Step 1: accumulator=0,  current=1  → return 0+1  = 1
// Step 2: accumulator=1,  current=2  → return 1+2  = 3
// Step 3: accumulator=3,  current=3  → return 3+3  = 6
// Step 4: accumulator=6,  current=4  → return 6+4  = 10
// Step 5: accumulator=10, current=5  → return 10+5 = 15
// Result: 15

Reduce Syntax

array.reduce((accumulator, currentValue, index, array) => {
  // Return the new accumulator value
}, initialValue);
  • accumulator: The result so far
  • currentValue: Current element being processed
  • initialValue: Starting value for accumulator

Practical Examples

// Find maximum number
const numbers = [45, 23, 78, 12, 99, 34];
const max = numbers.reduce((max, current) => {
  return current > max ? current : max;
}, numbers[0]);
console.log(max);  // 99

// Count occurrences
const fruits = ["apple", "banana", "apple", "orange", "banana", "apple"];
const count = fruits.reduce((acc, fruit) => {
  acc[fruit] = (acc[fruit] || 0) + 1;
  return acc;
}, {} as Record);

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

// Calculate total price

const cart[] = [
  { name: "Laptop", price: 999, quantity: 1 },
  { name: "Mouse", price: 25, quantity: 2 },
  { name: "Keyboard", price: 75, quantity: 1 }
];

const total = cart.reduce((sum, item) => {
  return sum + (item.price * item.quantity);
}, 0);

console.log(`Total: $${total}`);  // Total: $1124

// Flatten array of arrays
const nested[] = [[1, 2], [3, 4], [5, 6]];
const flat = nested.reduce((acc, current) => {
  return acc.concat(current);
}, []);

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

---

Part 5: The find() Method

find() returns the first element that passes a test.
const numbers = [5, 12, 8, 130, 44];

// Find first number > 10
const found | undefined = numbers.find(num => num > 10);
console.log(found);  // 12

// Find first number > 200
const notFound = numbers.find(num => num > 200);
console.log(notFound);  // undefined

// Find user by ID
const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 3, name: "Charlie" }
];

const user = users.find(user => user.id === 2);
console.log(user);  // { id: 2, name: "Bob" }

---

Part 6: The findIndex() Method

findIndex() returns the index of the first element that passes a test.
const numbers = [5, 12, 8, 130, 44];

const index = numbers.findIndex(num => num > 10);
console.log(index);  // 1 (found at position 1)

const notFoundIndex = numbers.findIndex(num => num > 200);
console.log(notFoundIndex);  // -1 (not found)

// Remove item from array by ID
const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 3, name: "Charlie" }
];

const indexToRemove = users.findIndex(user => user.id === 2);
if (indexToRemove !== -1) {
  users.splice(indexToRemove, 1);
}

console.log(users);  // Alice and Charlie remain

---

Part 7: The some() Method

some() checks if at least one element passes a test.
const numbers = [1, 3, 5, 7, 8];

// Are any numbers even?
const hasEven = numbers.some(num => num % 2 === 0);
console.log(hasEven);  // true (8 is even)

// Are any numbers negative?
const hasNegative = numbers.some(num => num < 0);
console.log(hasNegative);  // false

// Check if any user is admin

const users[] = [
  { name: "Alice", isAdmin: false },
  { name: "Bob", isAdmin: true },
  { name: "Charlie", isAdmin: false }
];

const hasAdmin = users.some(user => user.isAdmin);
console.log(hasAdmin);  // true

---

Part 8: The every() Method

every() checks if all elements pass a test.
const numbers = [2, 4, 6, 8, 10];

// Are all numbers even?
const allEven = numbers.every(num => num % 2 === 0);
console.log(allEven);  // true

const numbers2 = [2, 4, 5, 8];
const allEven2 = numbers2.every(num => num % 2 === 0);
console.log(allEven2);  // false (5 is odd)

// Check if all users are adults

const users[] = [
  { name: "Alice", age: 30 },
  { name: "Bob", age: 25 },
  { name: "Charlie", age: 35 }
];

const allAdults = users.every(user => user.age >= 18);
console.log(allAdults);  // true

---

Part 9: Method Chaining

You can chain array methods together:

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Get even numbers, double them, then sum them
const result = numbers
  .filter(num => num % 2 === 0)  // [2, 4, 6, 8, 10]
  .map(num => num * 2)           // [4, 8, 12, 16, 20]
  .reduce((sum, num) => sum + num, 0);  // 60

console.log(result);  // 60

Real-World Example

const products[] = [
  { name: "Laptop", price: 999, category: "electronics", inStock: true },
  { name: "Mouse", price: 25, category: "electronics", inStock: true },
  { name: "Desk", price: 200, category: "furniture", inStock: false },
  { name: "Chair", price: 150, category: "furniture", inStock: true },
  { name: "Keyboard", price: 75, category: "electronics", inStock: true }
];

// Get names of in-stock electronics under $100
const affordableElectronics = products
  .filter(p => p.category === "electronics")
  .filter(p => p.inStock)
  .filter(p => p.price < 100)
  .map(p => p.name);

console.log(affordableElectronics);  // ["Mouse", "Keyboard"]

// Calculate total value of in-stock items
const totalValue = products
  .filter(p => p.inStock)
  .reduce((sum, p) => sum + p.price, 0);

console.log(`Total value: $${totalValue}`);  // Total value: $1249

---

Part 10: Comparison with Traditional Loops

Using Loops (Old Way)

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

// Double even numbers
const result = [];
for (const num of numbers) {
  if (num % 2 === 0) {
    result.push(num * 2);
  }
}
console.log(result);  // [4, 8]

Using Array Methods (Modern Way)

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

const result = numbers
  .filter(num => num % 2 === 0)
  .map(num => num * 2);

console.log(result);  // [4, 8]
Benefits of array methods:
  • More concise and readable
  • No need to manage temporary variables
  • Less prone to errors
  • Functional programming style
  • Each method returns a new array (immutability)

---

Practice Exercises

Exercise 1: Student Grades

Given an array of student objects with grades, find the average grade of passing students (grade >= 60).

Exercise 2: Product Filter

Filter products by multiple criteria and calculate total discounted price.

Exercise 3: Word Statistics

Count words, find longest word, and filter words by length from a text string.

Exercise 4: Data Transformation

Transform an array of users into a lookup object (dictionary) with ID as key.

Exercise 5: Shopping Cart

Implement cart operations: add, remove, update quantity, calculate total, apply discount.

Exercise 6: Array Flattening

Flatten a deeply nested array using reduce.

---

Common Mistakes

Mistake 1: Forgetting to Return in map/filter

const numbers = [1, 2, 3];

// ❌ WRONG - No return
const doubled = numbers.map(num => {
  num * 2;  // Missing return!
});
console.log(doubled);  // [undefined, undefined, undefined]

// ✅ CORRECT
const doubled = numbers.map(num => {
  return num * 2;
});
// OR (implicit return with arrow functions)
const doubled = numbers.map(num => num * 2);

Mistake 2: Mutating Original Array

const numbers = [1, 2, 3];

// ❌ DON'T DO THIS - mutates original
numbers.forEach(num => {
  num = num * 2;  // Doesn't change array!
});

// ✅ CORRECT - use map for transformation
const doubled = numbers.map(num => num * 2);

Mistake 3: Wrong Initial Value in reduce

const numbers = [1, 2, 3];

// ❌ WRONG - No initial value, could cause issues
const sum = numbers.reduce((acc, num) => acc + num);

// ✅ CORRECT - Always provide initial value
const sum = numbers.reduce((acc, num) => acc + num, 0);

---

Key Concepts Summary

Method Returns Purpose
map() New array Transform each element
filter() New array Select elements that pass test
reduce() Single value Combine elements into one value
find() Element or undefined Find first match
findIndex() Number Find index of first match
some() Boolean Check if any element passes
every() Boolean Check if all elements pass

What You Learned

  • ✅ How to transform arrays with map()
  • ✅ How to filter arrays with filter()
  • ✅ How to reduce arrays to single values with reduce()
  • ✅ How to find elements with find() and findIndex()
  • ✅ How to test arrays with some() and every()
  • ✅ How to chain methods for complex operations
  • ✅ When to use array methods vs traditional loops

What's Next?

In the next lesson, you'll learn about error handling - how to deal with errors gracefully using try/catch blocks and create your own custom errors!