Mutation Methods
These methods modify the original array in place. Be careful when using them in functional or React-style code where immutability matters.
const arr = [3, 1, 4, 1, 5];
// push / pop β end of array
arr.push(9, 2); // [3,1,4,1,5,9,2] β returns new length: 7
arr.pop(); // [3,1,4,1,5,9] β returns 9
// unshift / shift β beginning of array
arr.unshift(0); // [0,3,1,4,1,5,9] β returns new length
arr.shift(); // [3,1,4,1,5,9] β returns 0
// splice(start, deleteCount, ...items)
const removed = arr.splice(2, 2, 10, 20);
// removed = [4, 1], arr = [3, 1, 10, 20, 5, 9]
// sort β lexicographic by default! use comparator
[3, 1, 10, 2].sort(); // [1, 10, 2, 3] β wrong for numbers!
[3, 1, 10, 2].sort((a, b) => a-b); // [1, 2, 3, 10] β ascending
// reverse β in place
[1, 2, 3].reverse(); // [3, 2, 1]
// fill(value, start?, end?)
new Array(5).fill(0); // [0, 0, 0, 0, 0]
[1,2,3,4,5].fill(0, 1, 3); // [1, 0, 0, 4, 5]
// copyWithin(target, start, end)
[1,2,3,4,5].copyWithin(0, 3); // [4, 5, 3, 4, 5] (copies positions 3-4 to start)
ES2023 added toSorted(), toReversed(), toSpliced(), and with(index, value) β all return new arrays without modifying the original. Use them in React state updates and functional code.
Access Methods
These methods return information about or derived slices of the array without modifying it.
const fruits = ['apple', 'banana', 'cherry', 'banana'];
// slice(start, end) β new array, does not mutate
console.log(fruits.slice(1, 3)); // ['banana', 'cherry']
console.log(fruits.slice(-2)); // ['cherry', 'banana']
// concat β merge arrays into a new array
const more = fruits.concat(['date', 'elderberry']);
// ['apple','banana','cherry','banana','date','elderberry']
// join(separator) β array to string
console.log(fruits.join(', ')); // 'apple, banana, cherry, banana'
console.log([1,2,3].join('-')); // '1-2-3'
// indexOf / lastIndexOf
console.log(fruits.indexOf('banana')); // 1
console.log(fruits.lastIndexOf('banana')); // 3
console.log(fruits.indexOf('grape')); // -1
// includes (uses SameValueZero β handles NaN correctly)
console.log(fruits.includes('cherry')); // true
console.log([NaN].includes(NaN)); // true β unlike indexOf!
// at(index) β supports negative indices (ES2022)
console.log(fruits.at(0)); // 'apple'
console.log(fruits.at(-1)); // 'banana' (last)
// flat / flatMap
console.log([[1,2],[3,[4]]].flat()); // [1, 2, 3, [4]]
console.log([[1,2],[3,[4]]].flat(2)); // [1, 2, 3, 4]
Iteration Methods
Iteration methods accept a callback and process each element. They are the heart of functional-style JavaScript.
const nums = [1, 2, 3, 4, 5, 6];
// forEach β side effects; returns undefined
nums.forEach((n, i) => console.log(i, n));
// map β transform; returns new array (same length)
const doubled = nums.map(n => n * 2);
// [2, 4, 6, 8, 10, 12]
// filter β keep matching elements; returns new array
const evens = nums.filter(n => n % 2 === 0);
// [2, 4, 6]
// reduce(callback, initialValue) β accumulate to single value
const sum = nums.reduce((acc, n) => acc + n, 0); // 21
const product = nums.reduce((acc, n) => acc * n, 1); // 720
// reduceRight β same as reduce, but right to left
['a','b','c'].reduceRight((acc, s) => acc + s, ''); // 'cba'
// find β first match or undefined
const firstEven = nums.find(n => n % 2 === 0); // 2
// findIndex β index of first match or -1
const idx = nums.findIndex(n => n > 4); // 4
// findLast / findLastIndex (ES2023)
const lastEven = nums.findLast(n => n % 2 === 0); // 6
const lastEvenIdx = nums.findLastIndex(n => n % 2 === 0); // 5
// some β returns true if ANY element passes test
console.log(nums.some(n => n > 5)); // true
console.log(nums.some(n => n > 10)); // false
// every β returns true if ALL elements pass test
console.log(nums.every(n => n > 0)); // true
console.log(nums.every(n => n > 3)); // false
// flatMap β map + flat(1) in one pass
const sentences = ['hello world', 'foo bar'];
const words = sentences.flatMap(s => s.split(' '));
// ['hello', 'world', 'foo', 'bar']
[2, 4, 6, 8, 10, 12] [2, 4, 6] 21 720 2 4 true false true false ['hello', 'world', 'foo', 'bar']
Static Array Methods
// Array.isArray β reliable type check
Array.isArray([]); // true
Array.isArray('string'); // false
Array.isArray({ length: 2 }); // false
// Array.from β create from iterable or array-like
Array.from('abc'); // ['a', 'b', 'c']
Array.from({ length: 3 }, (_, i) => i); // [0, 1, 2]
Array.from(new Set([1, 2, 2, 3])); // [1, 2, 3]
// Array.of β create from arguments (unlike new Array)
Array.of(7); // [7] (NOT [empty Γ 7])
Array.of(1, 2, 3); // [1, 2, 3]
Method Chaining
const orders = [
{ id: 1, product: 'Laptop', price: 999, qty: 1, status: 'shipped' },
{ id: 2, product: 'Mouse', price: 29, qty: 3, status: 'pending' },
{ id: 3, product: 'Monitor', price: 399, qty: 2, status: 'shipped' },
{ id: 4, product: 'Keyboard',price: 79, qty: 2, status: 'pending' },
{ id: 5, product: 'Webcam', price: 149, qty: 1, status: 'shipped' }
];
// Find total revenue from shipped orders, sorted by value
const report = orders
.filter(o => o.status === 'shipped') // keep shipped
.map(o => ({ ...o, total: o.price * o.qty })) // add total field
.sort((a, b) => b.total - a.total) // sort descending
.map(o => `${o.product}: $${o.total}`); // format
console.log(report);
// ['Laptop: $999', 'Monitor: $798', 'Webcam: $149']
// Total revenue
const totalRevenue = orders
.filter(o => o.status === 'shipped')
.reduce((sum, o) => sum + o.price * o.qty, 0);
console.log(totalRevenue); // 1946
Put filter before map in a chain β this reduces the number of elements that subsequent steps need to process. For very large arrays, consider a single-pass reduce instead of multiple chained methods.
Choosing the Right Method
| Goal | Method | Mutates? | Returns |
|---|---|---|---|
| Run side effects | forEach | No | undefined |
| Transform each element | map | No | New array (same length) |
| Keep matching elements | filter | No | New array (shorter) |
| Accumulate to one value | reduce | No | Single value |
| Find first match | find | No | Element or undefined |
| Find index of first match | findIndex | No | Index or -1 |
| Check any element passes | some | No | boolean |
| Check all elements pass | every | No | boolean |
| Flatten nested arrays | flat(depth) | No | New array |
| Map + flatten one level | flatMap | No | New array |
| Add to end | push | Yes | New length |
| Remove from end | pop | Yes | Removed element |
| Sort in place | sort | Yes | Sorted array (same ref) |
| Sort without mutating | toSorted | No | New sorted array |
Performance Tips
some and every short-circuit: some stops at the first true result; every stops at the first false. Use them instead of filter or forEach when you only need a boolean answer β this can save iterating thousands of elements.
Interview Questions
- What is the difference between
mapandforEach? - When would you use
reduceinstead of aforloop? - How does
somediffer fromevery? - What does
flatMapdo thatmapalone cannot? - Name three mutation methods and three non-mutation equivalents.
- Why should you put
filterbeforemapin a method chain?
ποΈ Practical Exercise
Using only array methods (no loops), write a pipeline that takes an array of strings representing scores like ["Alice:95", "Bob:82", "Carol:88", "Dave:70"] and produces:
- An array of objects
{ name, score }parsed from each string. - Filter to only scores above 80.
- Sort by score descending.
- Return a formatted leaderboard string like
"1. Alice (95)\n2. Carol (88)".
π₯ Challenge Exercise
Implement the following array methods from scratch (without using the originals): myMap(arr, fn), myFilter(arr, fn), and myReduce(arr, fn, initial). Then implement myFlatMap(arr, fn) using only your myMap and a flatten helper. All implementations must handle edge cases (empty arrays, missing initial value for reduce).
Frequently Asked Questions
- Can I use
mapto iterate without using its return value? - Technically yes, but you should use
forEachinstead. Usingmapfor side effects creates a throw-away array, wastes memory, and confuses readers about your intent. - Does
filtermodify the original array? - No.
filteralways returns a new array. The original is untouched. This makes it safe to use in functional and React patterns. - What is the initial value in
reduceand why is it important? - The initial value is the starting accumulator. Without it,
reduceuses the first element as the accumulator and starts iterating from the second. This can cause bugs with empty arrays (it throws a TypeError) β always provide an initial value. - How does
sorthandle strings by default? - Without a comparator,
sortconverts each element to a string and compares by Unicode code points. This means[10, 9, 2].sort()returns[10, 2, 9]. Always pass(a, b) => a - bfor numeric sorting. - What is the difference between
findandfilter? findreturns the first matching element (and stops searching).filterreturns all matching elements in a new array and processes every element. Usefindwhen you expect a unique result.
π Summary
- Mutation methods (
push,pop,sort,splice, etc.) change the array in place. - ES2023 added non-mutating counterparts:
toSorted,toReversed,toSpliced,with. - Iteration methods (
map,filter,reduce, etc.) return new arrays or values. - Use
mapto transform,filterto select,reduceto accumulate. someandeveryshort-circuit for early exit β prefer them for boolean checks.- Put
filterbeforemapin chains to reduce unnecessary iterations. - Always pass a comparator to
sort()for numeric or custom sort order. - Always provide an initial value to
reduce()to handle empty arrays safely.