Skip to content
beginner Phase 4 · JavaScript Fundamentals

Arrays & Higher-Order Methods

Master array methods — map, filter, reduce, find, sort, and array destructuring.

1h
0 problems
Topic Progress 0%

Map, Filter, and Reduce

Map, Filter, and Reduce

The map, filter, and reduce methods are the cornerstone of functional array processing in JavaScript. They allow you to transform, select, and aggregate data without writing manual loops or mutating the original array.

map

map creates a new array by applying a callback to every element. The callback receives the current element, its index, and the array itself:

const prices = [10.99, 24.99, 5.49];
const withTax = prices.map(price => +(price * 1.08).toFixed(2));
console.log(withTax); // [11.87, 26.99, 5.93]

A common pattern is mapping an array of objects to extract a single property:

const users = [
  { name: 'Alice', age: 30 },
  { name: 'Bob', age: 25 },
  { name: 'Carol', age: 35 }
];
const names = users.map(u => u.name);
console.log(names); // ['Alice', 'Bob', 'Carol']

filter

filter returns a new array containing only elements for which the callback returns true. The callback signature is identical to map:

const numbers = [1, 2, 3, 4, 5, 6];
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4, 6]

You can combine filter and map for chained transformations:

const products = [
  { name: 'Laptop', price: 999 },
  { name: 'Phone', price: 699 },
  { name: 'Tablet', price: 399 },
  { name: 'Watch', price: 249 }
];
const expensiveNames = products
  .filter(p => p.price > 400)
  .map(p => p.name);
console.log(expensiveNames); // ['Laptop', 'Phone']

reduce

reduce processes all elements into a single value. It takes a callback with an accumulator and current value, plus an optional initial value:

const cart = [
  { item: 'Shirt', qty: 2, price: 25 },
  { item: 'Jeans', qty: 1, price: 60 },
  { item: 'Socks', qty: 3, price: 5 }
];
const total = cart.reduce((sum, item) => sum + item.qty * item.price, 0);
console.log(total); // 125

reduce can also build objects or arrays. For example, grouping items by category:

const items = [
  { name: 'Apple', type: 'fruit' },
  { name: 'Carrot', type: 'vegetable' },
  { name: 'Banana', type: 'fruit' },
  { name: 'Broccoli', type: 'vegetable' }
];
const grouped = items.reduce((acc, item) => {
  acc[item.type] = acc[item.type] || [];
  acc[item.type].push(item.name);
  return acc;
}, {});
console.log(grouped);
// { fruit: ['Apple', 'Banana'], vegetable: ['Carrot', 'Broccoli'] }

All three methods never mutate the original array, making your code predictable and easier to debug.

Find, Some, and Every

Find, Some, and Every

These methods let you search and test array elements using predicate callbacks. They are more readable and expressive than for loops with break or flag variables.

find

find returns the first element that satisfies the test, or undefined if none match:

const employees = [
  { id: 1, name: 'Alice', role: 'Engineer' },
  { id: 2, name: 'Bob', role: 'Designer' },
  { id: 3, name: 'Carol', role: 'Engineer' }
];
const engineer = employees.find(e => e.role === 'Engineer');
console.log(engineer); // { id: 1, name: 'Alice', role: 'Engineer' }

If you need the index of the match, use findIndex:

const idx = employees.findIndex(e => e.name === 'Bob');
console.log(idx); // 1

some

some returns true if at least one element passes the test. It short-circuits on the first match:

const ages = [22, 17, 35, 14, 28];
const hasAdult = ages.some(age => age >= 18);
console.log(hasAdult); // true

A practical use case is checking for permission flags:

const userRoles = ['viewer', 'editor'];
const canPublish = userRoles.some(role => role === 'admin' || role === 'editor');
console.log(canPublish); // true

every

every returns true only if all elements pass the test. It also short-circuits on the first failure:

const scores = [85, 92, 78, 95];
const allPassing = scores.every(score => score >= 60);
console.log(allPassing); // true

You can validate form fields with every:

const fields = [
  { name: 'email', value: 'alice@example.com' },
  { name: 'password', value: 's3cure!' },
  { name: 'age', value: '28' }
];
const allFilled = fields.every(f => f.value.trim().length > 0);
console.log(allFilled); // true

Comparison Table

Method Returns Short-circuits Use case
find First match Yes Retrieve a specific element
findIndex Index of match Yes Locate position in array
some Boolean Yes Check if any element qualifies
every Boolean Yes Validate all elements qualify

These methods work exclusively with arrays, but you can convert array-like objects (NodeList, arguments) using Array.from or the spread operator before calling them.

Sort, Flat, and Array.from

Sort, Flat, and Array.from

These utility methods handle reordering, flattening, and creating arrays from non-array iterables.

sort

sort sorts the array in place and returns the same reference. By default, it sorts lexicographically (as strings), so always provide a comparator for numbers:

const nums = [40, 1, 5, 200];
nums.sort();
console.log(nums); // [1, 200, 40, 5] — not what you want!

nums.sort((a, b) => a - b);
console.log(nums); // [1, 5, 40, 200]

To sort without mutating the original, spread into a new array first:

const original = [3, 1, 4, 1, 5];
const sorted = [...original].sort((a, b) => a - b);
console.log(sorted);  // [1, 1, 3, 4, 5]
console.log(original); // [3, 1, 4, 1, 5] — unchanged

Sorting objects by a property:

const users = [
  { name: 'Alice', score: 88 },
  { name: 'Bob', score: 95 },
  { name: 'Carol', score: 72 }
];
const ranked = [...users].sort((a, b) => b.score - a.score);
console.log(ranked.map(u => u.name)); // ['Bob', 'Alice', 'Carol']

flat

flat flattens nested arrays by one level by default. Pass Infinity to flatten all levels:

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

A common use case is flattening results from nested API calls:

const departments = [
  ['Alice', 'Bob'],
  ['Carol'],
  ['Dave', 'Eve', 'Frank']
];
const allEmployees = departments.flat();
console.log(allEmployees); // ['Alice', 'Bob', 'Carol', 'Dave', 'Eve', 'Frank']

flatMap combines map followed by flat(1) for performance:

const sentences = ['Hello world', 'Goodbye moon'];
const words = sentences.flatMap(s => s.split(' '));
console.log(words); // ['Hello', 'world', 'Goodbye', 'moon']

Array.from

Array.from creates a new array from array-like objects (NodeList, arguments, Sets, Maps) or iterables. It accepts an optional mapping function as the second argument:

// Convert a NodeList to an array
const buttons = document.querySelectorAll('button');
const btnArray = Array.from(buttons);
btnArray.forEach(btn => btn.classList.add('active'));

Create an array of sequential numbers:

const range = Array.from({ length: 5 }, (_, i) => i + 1);
console.log(range); // [1, 2, 3, 4, 5]

Clone and transform in one step:

const original = [1, 2, 3];
const doubled = Array.from(original, n => n * 2);
console.log(doubled); // [2, 4, 6]

Convert a string to an array of characters:

const chars = Array.from('hello');
console.log(chars); // ['h', 'e', 'l', 'l', 'o']

Unlike Array.prototype.slice, Array.from works with any iterable, making it essential for working with ES6 data structures like Set and Map.

Destructuring and Spread

Destructuring and Spread

Destructuring assignment and the spread operator provide concise syntax for extracting and combining array values.

Basic Destructuring

Extract values from the left side of the assignment:

const rgb = [255, 128, 0];
const [red, green, blue] = rgb;
console.log(red, green, blue); // 255 128 0

Skipping Elements

Use commas to skip positions you do not need:

const coords = [10, 20, 30, 40];
const [x, , z] = coords;
console.log(x, z); // 10 30

Rest Pattern

Collect remaining elements into a new array using the rest operator (...):

const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(rest);  // [2, 3, 4, 5]

Default Values

Provide fallback values when array elements might be undefined:

const [a = 10, b = 20, c = 30] = [1, 2];
console.log(a, b, c); // 1 2 30

Swapping Variables

Swap two variables without a temporary variable:

let x = 1;
let y = 2;
[x, y] = [y, x];
console.log(x, y); // 2 1

Spread Operator

Spread expands an iterable into individual elements. Common uses:

// Merging arrays
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const merged = [...arr1, ...arr2];
console.log(merged); // [1, 2, 3, 4, 5, 6]
// Adding elements without mutation
const base = ['a', 'b'];
const extended = [...base, 'c', 'd'];
console.log(extended); // ['a', 'b', 'c', 'd']
console.log(base);     // ['a', 'b'] — unchanged

Destructuring in Function Parameters

Accept arrays directly as function arguments:

function greet([first, last]) {
  return `Hello, ${first} ${last}!`;
}
console.log(greet(['Alice', 'Smith'])); // 'Hello, Alice Smith!'

Practical Example — Returning Multiple Values

Destructuring shines when a function needs to return multiple values:

function getStats(numbers) {
  const sorted = [...numbers].sort((a, b) => a - b);
  const sum = sorted.reduce((acc, n) => acc + n, 0);
  return [
    sorted[0],                     // min
    sorted[sorted.length - 1],     // max
    sum / sorted.length            // average
  ];
}

const [min, max, avg] = getStats([3, 1, 4, 1, 5, 9, 2, 6]);
console.log({ min, max, avg }); // { min: 1, max: 9, avg: 3.875 }

Destructuring and spread are syntax features, not methods, so they work with any array or iterable — no runtime overhead.

Quiz

1. What does the `map` method return?

Question 1 options

2. What is the key difference between `some` and `every`?

Question 2 options

3. Given `const arr = [3, 1, 4, 1, 5]; const sorted = [...arr].sort((a, b) => a - b);`, what are the values of `arr` and `sorted`?

Question 3 options

Flashcards

Question

What is the difference between `map` and `forEach`?

Answer

map returns a new array of transformed values and is designed for functional pipelines. forEach executes a side effect on each element and always returns undefined. Use map when you need the output; use forEach for logging, DOM updates, or other side effects.

Question

When should you use `reduce` instead of `map` and `filter`?

Answer

Use reduce when you need to accumulate a single output from the entire array — such as computing a sum, building a lookup object, or grouping data. Map produces a 1-to-1 array, and filter produces a subset. Reduce handles any aggregation that requires maintaining state between iterations.

Question

Does `sort` mutate the original array? How do you avoid mutation?

Answer

Yes, sort mutates the array in place and returns the same reference. To avoid mutation, spread the array into a new copy before sorting: const sorted = [...arr].sort((a, b) => a - b).

Revision Notes

Key Takeaways

  • 1. map, filter, and reduce never mutate the original array — they return new arrays or values, making functional pipelines safe and composable
  • 2. Always provide a comparator function to sort for numeric data; the default sort is lexicographic and produces unexpected results for numbers
  • 3. find returns the first matching element; some and every short-circuit, stopping as soon as the result is determined, which improves performance
  • 4. flat(n) flattens n levels deep; flat(Infinity) flattens all nesting levels; flatMap combines map + flat(1) for efficiency
  • 5. Array.from converts array-likes (NodeList, arguments, Sets) into true arrays with built-in mapping, while spread (...) works for any iterable
  • 6. Destructuring extracts values by position; use rest (...) to collect remaining elements; default values handle undefined gracefully

Interview Tips

  • Be ready to implement map, filter, and reduce from scratch — interviewers often ask for polyfills to test your understanding of callbacks and accumulator patterns
  • Know when to use which method: map for transformation, filter for selection, reduce for aggregation, find for lookup, some/every for validation
  • Explain that sort mutates in place and why spreading first is the best practice for non-destructive sorting
  • Discuss short-circuiting behavior of some and every — it is a performance optimization, not just a language quirk
  • Be able to trace through a chained pipeline like arr.filter(...).map(...).reduce(...) and predict the output step by step
  • Mention that Array.from works with any iterable (strings, Sets, Maps, generators) whereas Array.from vs spread is a common trade-off for cloning

Cheat Sheet

map(cb) → new array of same length with transformed values | filter(cb) → new array with elements where cb returns true | reduce(cb, init) → single accumulated value | find(cb) → first match or undefined | findIndex(cb) → index of first match or -1 | some(cb) → true if any element passes | every(cb) → true if all pass | sort(cb) → mutates, sorts in place | flat(n) → flattens n levels | flatMap(cb) → map then flat(1) | Array.from(iterable, cb) → array from any iterable | [...arr] → shallow copy via spread | const [a, ...rest] = arr → destructuring with rest pattern