Functional array methods for transforming, filtering, and aggregating data without explicit loops.
**map — transform every element**
```javascript
const nums = [1, 2, 3, 4];
// map returns a NEW array — does not modify the original
const doubled = nums.map(x => x * 2); // [2, 4, 6, 8]
console.log(nums); // [1, 2, 3, 4] — unchanged
// map callback gets (element, index, array):
const tagged = nums.map((x, i) => `${i}:${x}`); // ['0:1', '1:2', '2:3', '3:4']
// Common mistake — forgetting the implicit return in arrow functions:
const bad = nums.map(x => { x * 2 }); // [undefined, undefined, ...] — {} means block
const good = nums.map(x => x * 2); // [2, 4, 6, 8] — expression body
const good2 = nums.map(x => ({ value: x * 2 })); // return object — wrap in ()
```
**filter — keep elements that pass a test**
```javascript
const evens = nums.filter(x => x % 2 === 0); // [2, 4]
// filter also returns a new array:
const words = ['', 'hello', null, 'world', undefined];
const clean = words.filter(Boolean); // ['hello', 'world'] — filter(Boolean) removes falsy
```
**reduce — fold into a single value**
```javascript
// reduce(callback, initialValue)
// callback gets (accumulator, currentValue, index, array)
const sum = nums.reduce((acc, x) => acc + x, 0); // 10
const max = nums.reduce((acc, x) => x > acc ? x : acc, -Infinity); // 4
// Always provide an initial value — without it, reduce fails on empty arrays:
[].reduce((acc, x) => acc + x); // TypeError: Reduce of empty array
[].reduce((acc, x) => acc + x, 0); // 0 — safe
```
**forEach — side effects only**
```javascript
// forEach returns undefined — never use it to build a new array
nums.forEach(x => console.log(x)); // prints 1 2 3 4
// BAD:
const result = nums.forEach(x => x * 2); // undefined — don't do this
// GOOD: use map when you need a new array
```
**Mutation warning: sort() and reverse() modify in place**
```javascript
const arr = [3, 1, 4, 1, 5];
// sort and reverse MUTATE the original:
arr.sort((a, b) => a - b); // arr is now [1, 1, 3, 4, 5]
arr.reverse(); // arr is now [5, 4, 3, 1, 1]
// To sort without mutation (ES2023 toSorted/toReversed, or copy first):
const sorted = [...arr].sort((a, b) => a - b); // safe — spread copies
const rev = arr.toReversed(); // ES2023, returns new array
```
**Chaining array methods**
Because map/filter return new arrays, you can chain them:
```javascript
const users = [
{ name: 'Alice', age: 25, active: true },
{ name: 'Bob', age: 17, active: true },
{ name: 'Carol', age: 32, active: false },
{ name: 'Dave', age: 28, active: true },
];
const result = users
.filter(u => u.active && u.age >= 18) // keep active adults
.map(u => u.name) // extract names
.sort(); // alphabetical
// ['Alice', 'Dave']
```
**reduce for grouping**
```javascript
const orders = [
{ category: 'food', amount: 30 },
{ category: 'tech', amount: 200 },
{ category: 'food', amount: 15 },
];
const totals = orders.reduce((acc, order) => {
acc[order.category] = (acc[order.category] ?? 0) + order.amount;
return acc;
}, {});
// { food: 45, tech: 200 }
```
**flatMap — map then flatten one level**
```javascript
const sentences = ['hello world', 'foo bar baz'];
// map gives array of arrays:
sentences.map(s => s.split(' ')); // [['hello','world'], ['foo','bar','baz']]
// flatMap maps then flattens one level:
sentences.flatMap(s => s.split(' ')); // ['hello', 'world', 'foo', 'bar', 'baz']
// flatMap is also useful for producing variable-length outputs:
const nums = [1, 2, 3];
nums.flatMap(x => x % 2 === 0 ? [x, x * 10] : [x]);
// [1, 2, 20, 3]
```
**Array.from — create arrays from other things**
```javascript
// From a NodeList (DOM elements)
Array.from(document.querySelectorAll('li'))
.map(el => el.textContent);
// From a Set (deduplication)
const unique = Array.from(new Set([1, 2, 2, 3, 3]));
// [1, 2, 3]
// From a string
Array.from('hello'); // ['h', 'e', 'l', 'l', 'o']
// With a mapping function (second argument):
Array.from({ length: 5 }, (_, i) => i * 2);
// [0, 2, 4, 6, 8]
```
**find, findIndex, some, every**
```javascript
const users = [
{ id: 1, name: 'Alice', age: 25 },
{ id: 2, name: 'Bob', age: 17 },
{ id: 3, name: 'Carol', age: 32 },
];
// find — first match, or undefined
users.find(u => u.id === 2); // { id: 2, name: 'Bob', age: 17 }
users.find(u => u.age > 50); // undefined
// findIndex — index of first match, or -1
users.findIndex(u => u.name === 'Carol'); // 2
// some — true if at least one matches
users.some(u => u.age < 18); // true (Bob is 17)
// every — true if all match
users.every(u => u.age >= 18); // false (Bob is 17)
```
**flat and flat(Infinity)**
```javascript
const nested = [1, [2, 3], [4, [5, 6]]];
nested.flat(); // [1, 2, 3, 4, [5, 6]] — one level
nested.flat(2); // [1, 2, 3, 4, 5, 6] — two levels
nested.flat(Infinity); // fully flatten, no matter how deep
```
**Mutation vs immutability reference**
```
Method Returns new array? Mutates original?
────────────── ───────────────── ─────────────────
map ✅ new array ❌ no
filter ✅ new array ❌ no
reduce ✅ single value ❌ no
flatMap ✅ new array ❌ no
flat ✅ new array ❌ no
slice ✅ new array ❌ no
concat ✅ new array ❌ no
sort ⚠ same reference ✅ yes — sorts in place
reverse ⚠ same reference ✅ yes — reverses in place
splice returns removed ✅ yes — modifies array
push/pop returns item/length ✅ yes
shift/unshift returns item/length ✅ yes
fill ⚠ same reference ✅ yes
toSorted ✅ new array ❌ no (ES2023)
toReversed ✅ new array ❌ no (ES2023)
```
**includes, indexOf, at**
```javascript
[1, 2, 3].includes(2); // true
[1, 2, NaN].includes(NaN); // true (unlike indexOf!)
[1, 2, 3].indexOf(2); // 1
[1, 2, NaN].indexOf(NaN); // -1 (uses ===, NaN !== NaN)
// at() — negative index support
const arr = [10, 20, 30, 40];
arr.at(-1); // 40 — last element
arr.at(-2); // 30 — second to last
```
Write a function that takes an array of numbers and returns a new array where each number is doubled. Use the map() method.
function doubleValues(numbers) {
}
console.log(doubleValues([1, 2, 3, 4, 5]));
Solution
function doubleValues(numbers) {
return numbers.map(n => n * 2);
}
console.log(doubleValues([1, 2, 3, 4, 5]));
Note
`map()` always returns a new array of the same length — it transforms each element without modifying the original.
Write a function that takes an array of numbers and returns only the positive ones. Use the filter() method.
function keepPositives(numbers) {
}
console.log(keepPositives([3, -1, 4, -1, -5, 9, -2, 6]));
Solution
function keepPositives(numbers) {
return numbers.filter(n => n > 0);
}
console.log(keepPositives([3, -1, 4, -1, -5, 9, -2, 6]));
Note
`filter()` returns a new array containing only the elements for which the callback returns true — the original is never modified.
Write a function that takes an array of numbers and returns their sum. Use the reduce() method.
function sumArray(numbers) {
}
console.log(sumArray([1, 2, 3, 4, 5]));
Solution
function sumArray(numbers) {
return numbers.reduce((acc, n) => acc + n, 0);
}
console.log(sumArray([1, 2, 3, 4, 5]));
Note
The second argument to `reduce()` is the initial value of the accumulator — always pass `0` for numeric sums to avoid bugs on empty arrays.
Write a function that takes an array of strings and a minimum length, and returns only the strings longer than that length.
function longWords(words, minLength) {
}
console.log(longWords(["cat", "elephant", "dog", "hippopotamus"], 4));
Solution
function longWords(words, minLength) {
return words.filter(w => w.length > minLength);
}
console.log(longWords(["cat", "elephant", "dog", "hippopotamus"], 4));
Note
Arrow functions in `filter()` callbacks can reference outer variables (like `minLength`) directly — this is a closure in action.
Write a function that takes an array of strings and returns a new array with each string converted to uppercase.
function toUpperCase(words) {
}
console.log(toUpperCase(["hello", "world", "javascript"]));
Solution
function toUpperCase(words) {
return words.map(w => w.toUpperCase());
}
console.log(toUpperCase(["hello", "world", "javascript"]));
Note
`map()` with a string method is the JS equivalent of a Python list comprehension: `[w.upper() for w in words]`.
Write a function that takes an array of numbers and returns the product of all elements (multiply them all together). Use reduce().
function product(numbers) {
}
console.log(product([1, 2, 3, 4, 5]));
Solution
function product(numbers) {
return numbers.reduce((acc, n) => acc * n, 1);
}
console.log(product([1, 2, 3, 4, 5]));
Note
The initial value for multiplication is `1`, not `0` — starting with `0` would make everything zero.
Write a function that takes an array of objects, each having a `name` property, and returns an array of just the names.
function getNames(people) {
}
const people = [
{ name: "Alice", age: 30 },
{ name: "Bob", age: 25 },
{ name: "Carol", age: 35 },
];
console.log(getNames(people));
Solution
function getNames(people) {
return people.map(p => p.name);
}
const people = [
{ name: "Alice", age: 30 },
{ name: "Bob", age: 25 },
{ name: "Carol", age: 35 },
];
console.log(getNames(people));
Note
Mapping over arrays of objects to extract a single property is one of the most common real-world uses of `map()`.
Write a function that takes an array of numbers, keeps only those greater than 10, and returns the squares of the remaining numbers.
function squaresAboveTen(numbers) {
}
console.log(squaresAboveTen([3, 15, 8, 20, 5, 12]));
Solution
function squaresAboveTen(numbers) {
return numbers
.filter(n => n > 10)
.map(n => n ** 2);
}
console.log(squaresAboveTen([3, 15, 8, 20, 5, 12]));
Note
Chaining `filter()` then `map()` is idiomatic JS — filter first to reduce the array size, then map over what's left.
Write a function that takes an array of numbers and returns how many of them are even.
function countEven(numbers) {
}
console.log(countEven([1, 2, 3, 4, 5, 6, 7, 8]));
Solution
function countEven(numbers) {
return numbers.filter(n => n % 2 === 0).length;
}
console.log(countEven([1, 2, 3, 4, 5, 6, 7, 8]));
Note
Using `filter(...).length` is a clean pattern for counting elements that meet a condition — no manual counter variable needed.
Write a function that takes an array of strings and returns an object where each key is a first letter and the value is an array of all words starting with that letter.
function groupByFirstLetter(words) {
}
const result = groupByFirstLetter(["apple", "banana", "avocado", "blueberry", "cherry"]);
console.log(result);
Solution
function groupByFirstLetter(words) {
return words.reduce((acc, word) => {
const letter = word[0];
if (!acc[letter]) acc[letter] = [];
acc[letter].push(word);
return acc;
}, {});
}
const result = groupByFirstLetter(["apple", "banana", "avocado", "blueberry", "cherry"]);
console.log(result);
Note
`reduce()` with an object `{}` as the initial accumulator is the standard JS pattern for grouping — each iteration adds to the growing object.