Why Array Methods?
Array methods
are built-in functions on JavaScript’s Array
type that help you transform, search, aggregate, and mutate lists of values with clear, expressive code. Think of them as tiny superpowers that turn loops into readable one-liners.

Two Families: Mutating vs Non-Mutating
Mutating methods
: change the original array in place (push
,pop
,shift
,unshift
,splice
,sort
,reverse
,fill
,copyWithin
).Non-mutating methods
: return a new array or value and leave the original unchanged (map
,filter
,slice
,concat
,flat
,flatMap
,toSorted
,toReversed
,toSpliced
(new),reduce
,some
,every
,find
,findIndex
,findLast
,findLastIndex
,includes
,indexOf
,lastIndexOf
,join
).
A pure function
is one that has no side effects; many non-mutating methods help you write more functional code.
Try this exercise. Click the correct answer from the options.
Which method mutates the original array?
Click the option that best answers the question.
- `map`
- `filter`
- `slice`
- `splice`
Core Trio: map
, filter
, reduce
map(fn)
: transform each element, returning a new array of equal length.filter(fn)
: keep elements where the predicate is true.reduce(fn, init)
: fold the array into a single value (sum, object, map).
xxxxxxxxxx
// trio_demo.js
const nums = [1, 2, 3, 4, 5];
// Double then keep evens then sum
const doubled = nums.map(x => x * 2); // [2,4,6,8,10]
const evens = doubled.filter(x => x % 4 === 0); // [4,8]
const total = evens.reduce((acc, x) => acc + x, 0); // 12
console.log({ doubled, evens, total });
Build your intuition. Is this statement true or false?
map
can change the length of the resulting array.
Press true if you believe the statement is correct, or false otherwise.
reduce
Deep Dive (with Comments)
Build two results with a single pass using reduce: 1) sum of prices, 2) group items by category.
Accumulator
is the carry-over value you return each iteration; its shape is up to you.
xxxxxxxxxx
const items = [
{ name: 'apple', price: 1, category: 'fruit' },
{ name: 'banana', price: 2, category: 'fruit' },
{ name: 'carrot', price: 3, category: 'veg' },
{ name: 'kale', price: 2.5, category: 'veg' },
];
const init = { sum: 0, groups: {} };
const result = items.reduce((acc, item) => {
// accumulate sum
acc.sum += item.price;
// accumulate groups
const key = item.category;
if (!acc.groups[key]) acc.groups[key] = [];
acc.groups[key].push(item);
return acc; // always return the accumulator!
}, init);
console.log(result);
// { sum: 8.5, groups: { fruit: [...], veg: [...] } }
Let's test your knowledge. Could you figure out the right sequence for this list?
Put these steps of reduce
in order:
Press the below buttons in the order in which they should occur. Click on them again to un-select.
Options:
- Return the accumulator
- Run reducer on current item
- Initialize accumulator
- Move to next item
Searching: find
, some
, every
, includes
find(fn)
: first element that matches (orundefined
).some(fn)
: is there any match? returns boolean.every(fn)
: do all match? returns boolean.includes(value, fromIndex=0)
: direct value presence check (uses===
).
xxxxxxxxxx
// searchers.js
const users = [
{ id: 1, name: 'Ana', active: true },
{ id: 2, name: 'Bo', active: false },
{ id: 3, name: 'Cy', active: true }
];
console.log(users.find(u => !u.active)); // {id:2,...}
console.log(users.some(u => u.name.length > 2)); // true
console.log(users.every(u => typeof u.id === 'number')); // true
const letters = ['a','b','c'];
console.log(letters.includes('b')); // true
Are you sure you're getting this? Fill in the missing part by typing it in.
__________
returns the index of the first matching element, while find
returns the element itself.
Write the missing line below.
Slicing vs Splicing
slice(begin, end?)
: non-mutating copy of a subarray.splice(start, deleteCount, ...items)
: mutating insertion/removal.

xxxxxxxxxx
// slice_vs_splice.js
const a = [10,20,30,40,50];
const b = a.slice(1, 4); // [20,30,40], a unchanged
const removed = a.splice(2, 2); // removes 30,40 → a becomes [10,20,50]
console.log({ b, a, removed });
Immutable Newcomers
Modern JS adds non-mutating versions of classic mutators:
toSorted(compareFn?)
— likesort
but returns a new array.toReversed()
— likereverse
but non-mutating.toSpliced(start, deleteCount, ...items)
— likesplice
, but returns a new array.
xxxxxxxxxx
// immutable_methods.js
const a = [3,1,2];
const sorted = a.toSorted((x,y) => x - y); // [1,2,3], a still [3,1,2]
const rev = a.toReversed(); // [2,1,3], a still [3,1,2]
const sp2 = a.toSpliced(1,1,'X'); // [3,'X',2], a unchanged
console.log({ a, sorted, rev, sp2 });
Sorting Gotchas
sort
by default sorts lexicographically (as strings). Use a compare function
for numeric sort.

xxxxxxxxxx
// sort_numbers.js
const nums = [10, 2, 5, 30];
console.log(nums.sort()); // [10,2,30,5] (string sort!)
console.log(nums.sort((a,b)=>a-b)); // [2,5,10,30] (numeric)
Try this exercise. Click the correct answer from the options.
Which method both flattens and maps in one pass?
Click the option that best answers the question.
- `flat`
- `flatMap`
- `map`
- `concat`
flat
and flatMap
flat(depth=1)
: flattens nested arrays up todepth
.flatMap(fn)
: appliesmap
, then flattens one level.
xxxxxxxxxx
// flat_flatMap.js
const nested = [1, [2, [3, [4]]]];
console.log(nested.flat(1)); // [1,2,[3,[4]]]
console.log(nested.flat(2)); // [1,2,3,[4]]
console.log(nested.flat(99)); // [1,2,3,4]
const words = ["hi", "yo"];
console.log(words.map(w => w.split(""))); // [['h','i'], ['y','o']]
console.log(words.flatMap(w => w.split(""))); // ['h','i','y','o']
Joining and Splitting
join(sep=',')
: concatenate elements into a string.split(sep, limit?)
: (on strings) break into an array.
xxxxxxxxxx
// join_split.js
const cols = ['id','name','age'];
const header = cols.join(','); // 'id,name,age'
const row = "1,Ana,34".split(','); // ['1','Ana','34']
console.log({ header, row });
Aggregations You’ll Use Daily
These are every day aggregations you'll encounter daily, with examples.
xxxxxxxxxx
// Sum/avg with reduce
const amounts = [4, 10, 7];
const sum = amounts.reduce((a,b)=>a+b, 0);
const avg = sum / amounts.length;
// Max/min w/o Math.max(...spread) on huge arrays (safer for size)
const max = amounts.reduce((m,x)=> x>m?x:m, -Infinity);
const min = amounts.reduce((m,x)=> x<m?x:m, Infinity);
// Unique values (dedupe) preserving first occurrence
const arr = [3,5,3,2,5,9];
const uniq = arr.filter((v,i,src) => src.indexOf(v) === i);
// Frequency map
const freq = arr.reduce((m,x)=> (m[x]=(m[x]||0)+1, m), {});
console.log({ sum, avg, max, min, uniq, freq });
Try this exercise. Is this statement true or false?
indexOf
works for objects by structural equality.
Press true if you believe the statement is correct, or false otherwise.
Insertion & Removal Patterns (Immutable)
xxxxxxxxxx
// immutable_crud.js
const a = ['A','B','C','D'];
// Insert 'X' at index 2 (no mutation)
const insert2 = [a.slice(0,2), 'X', a.slice(2)];
// Remove at index 1
const remove1 = [a.slice(0,1), a.slice(2)];
// Replace index 3 with 'Z'
const replace3 = a.map((v,i)=> i===3 ? 'Z' : v);
console.log({ a, insert2, remove1, replace3 });
Performance & Pitfalls
O(n)
nature: many array ops traverse the entire list; avoid repeatedconcat
in loops—prefer onepush
per item or build thenjoin
.- Copy vs reference: methods like
slice
copy references for objects; nested structures still share inner objects. sort
is in-place; prefertoSorted
if immutability matters.
Project: CSV to Summary with Array Methods
Here, we parse a tiny CSV (no quotes/escapes) and summarize sales by product.
xxxxxxxxxx
const csv = `
product,price,qty
pen,1.50,4
pen,1.50,2
notebook,3.00,5
stapler,5.00,1
`.trim();
// 1) split lines, 2) split fields, 3) map to records, 4) reduce to summary
const [header, lines] = csv.split('\n');
const cols = header.split(',');
const records = lines
.map(line => line.split(','))
.map(parts => Object.fromEntries(parts.map((v,i)=>[cols[i], v])))
.map(r => ({ product: r.product, price: +r.price, qty: +r.qty })); // cast
const summary = records.reduce((acc, {product, price, qty}) => {
const t = (acc[product] ||= { units: 0, revenue: 0 });
t.units += qty;
t.revenue += price * qty;
return acc;
}, {});
console.log(records);
console.table(summary);
Try this exercise. Click the correct answer from the options.
Which sequence keeps the original array unchanged while returning a sorted copy?
Click the option that best answers the question.
- `arr.sort()`
- `[...arr].sort()`
- `arr.toSorted()`
- Both B and C
Defensive Patterns
- Use
Array.isArray(x)
to check arrayness;typeof [] === 'object'
. - Destructure with defaults to avoid
undefined
issues in callbacks. - Prefer
Number.isFinite
when parsing numeric arrays.
xxxxxxxxxx
// defensive.js
function sumIfNumbers(maybes) {
return maybes
.filter(x => Number.isFinite(x))
.reduce((a,b)=>a+b, 0);
}
console.log(sumIfNumbers([1, 2, NaN, Infinity, '3'])); // 3
Build your intuition. Fill in the missing part by typing it in.
The map
callback receives three parameters: currentValue
, index
, and __________
.
Write the missing line below.
Practice: Implement map
& filter
from Scratch
DO NOT use built-ins inside implementations; use simple loops.
Sparse array
has “holes” (missing indices). Native methods skip them; we mirrored that.
xxxxxxxxxx
// reimplement_map_filter.js
function myMap(arr, fn, thisArg) {
const out = new Array(arr.length);
for (let i = 0; i < arr.length; i++) {
// skip holes in sparse arrays to emulate native semantics
if (!(i in arr)) continue;
out[i] = fn.call(thisArg, arr[i], i, arr);
}
return out;
}
function myFilter(arr, fn, thisArg) {
const out = [];
for (let i = 0; i < arr.length; i++) {
if (!(i in arr)) continue;
if (fn.call(thisArg, arr[i], i, arr)) out.push(arr[i]);
}
return out;
}
const nums = [1, , 3, 4]; // note the hole at index 1
console.log(myMap(nums, x => x*2)); // [2, , 6, 8]
console.log(myFilter(nums, x => x > 2)); // [3,4]
One Pager Cheat Sheet
- Array methods are built-in functions on JavaScript’s
Array
type that facilitate transforming, searching, aggregating, and mutating lists of values with concise and readable code, essentially acting as small superpowers that simplify complex loops into one-liners. - The distinction between Mutating and Non-Mutating array methods is that the former alters the original array directly, while the latter creates a new array without changing the original, with many Non-Mutating methods supporting the principles of functional programming and pure functions.
- In short,
splice
mutates the original array by removing and/or inserting elements in the same array object and returning an array of the removed elements. - Core Trio consists of
map
,filter
, andreduce
, wheremap
is used to transform each element,filter
keeps elements based on a predicate, andreduce
combines all elements into a single value. - In JavaScript, the
map
method generates a new array of the samelength
as the original array, insertingundefined
values for elements that are not explicitly returned in the callback function. - Reduce allows you to perform multiple operations in a single pass, such as calculating the sum of prices and grouping items by category using an
accumulator
that carries over the value returned each iteration. - For
reduce
to work, you must initialize theaccumulator
first, run thereducer
on the current item andreturn
the updatedaccumulator
, and then move to the next item to repeat the process. - Key methods for searching in arrays include
find
,some
,every
, andincludes
, each serving different purposes such as retrieving the first matching element, checking for any matches, confirming all elements match, and directly checking for the presence of a value using strict equality. - The
findIndex
function in JavaScript returns the index of the first element that satisfies a predicate, as opposed to thefind
function which returns the element itself, with both methods stopping at the first match andfindIndex
returning-1
on no match, making it suitable for handling positions within arrays. - Slicing is a
non-mutating
operation that creates a copy of a subarray, while splicing is amutating
operation that allows for insertion and removal of elements within an array. - Immutable Newcomers in modern JS are non-mutating versions of classic mutators like
sort
,reverse
, andsplice
, which return new arrays instead of altering the original. - Sorting by default sorts lexicographically as strings, so a
compare function
should be used for numeric sort. flatMap
applies a mapping function to each element and then flattens the result by one level, avoiding intermediate array creation and reducing iterations, ultimately replacing the two-step process ofarr.map(fn).flat(1)
.flat
function flattens nested arrays up to a specified depth level, whileflatMap
function applies the map operation and then flattens one level.- The
join(sep=',')
function concatenates elements into a string, while thesplit(sep, limit?)
function on strings breaks them into an array. - Aggregations that are commonly used on a daily basis, including examples for reference.
indexOf
in JavaScript does not perform structural (deep) equality for objects; it relies on reference (identity) equality.- This section addresses insertion and removal patterns for immutable data structures.
- Performance & Pitfalls include the O(n) nature of many array operations, the distinction between copying vs referencing objects, and the importance of considering immutability when using the
sort
method. - Project is focused on converting a small CSV file with no quotes or escapes into a summary of sales by product using
Array Methods
. - Both B and C create a new (shallow) array copy of the original array using
slice()
and the spread syntax, then usesort
to return the sorted copy while leaving the original array unchanged. - When working with arrays, use
Array.isArray(x)
for array checking, use destructuring with defaults to avoidundefined
issues, and preferNumber.isFinite
when working with numeric arrays. - The
array
parameter in amap
callback provides access to the original array being iterated over, allowing for contextual operations within the callback function. - map and filter functions were implemented from scratch without using built-in methods, with the consideration of handling
sparse arrays
by skipping "holes" in the indices, similar to the behavior of native methods.