JavaScriptbeginner

JavaScript Array Methods Complete Guide

Master every essential JavaScript array method — map, filter, reduce, find, sort, flat, and more. Practical examples, performance tips, and functional programming patterns.

16 min read·Published Mar 5, 2026
arraysmethodsfunctional-programmingjavascript

Arrays Are Everywhere

Arrays are the most-used data structure in JavaScript. Whether you're rendering a list of users, processing API responses, or transforming data for a chart — arrays and their methods are what you reach for.

JavaScript arrays come with dozens of built-in methods. This guide covers every method you'll use regularly, organized by what they do.

┌────────────────────────────────────────────────────────┐
│             Array Methods by Category                   │
│                                                         │
│  Transform:    map, flatMap                             │
│  Filter:       filter, find, findIndex                  │
│  Test:         some, every, includes                    │
│  Reduce:       reduce, reduceRight                      │
│  Search:       indexOf, lastIndexOf, findLast           │
│  Sort:         sort, reverse, toSorted, toReversed      │
│  Add/Remove:   push, pop, shift, unshift, splice        │
│  Combine:      concat, flat, flatMap                    │
│  Iterate:      forEach, for...of, entries, keys, values │
│  Create:       Array.from, Array.of, fill               │
└────────────────────────────────────────────────────────┘

Transformation Methods

map — Transform Every Element

Creates a new array by applying a function to each element. The original array is unchanged.

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

const doubled = numbers.map((n) => n * 2);
// [2, 4, 6, 8, 10]

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

const names = users.map((user) => user.name);
// ['Alice', 'Bob']

// map passes three arguments: (element, index, array)
const indexed = ['a', 'b', 'c'].map((char, i) => `${i}: ${char}`);
// ['0: a', '1: b', '2: c']

Common mistake — using map when you don't need the return value:

// BAD — map creates a new array that's never used
users.map((user) => console.log(user.name));

// GOOD — use forEach for side effects
users.forEach((user) => console.log(user.name));

filter — Keep What Matches

Creates a new array with only elements that pass a test.

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

const evens = numbers.filter((n) => n % 2 === 0);
// [2, 4, 6, 8, 10]

const adults = users.filter((user) => user.age >= 18);

// Remove falsy values
const mixed = [0, 'hello', '', null, 42, undefined, false, 'world'];
const truthy = mixed.filter(Boolean);
// ['hello', 42, 'world']

// Remove duplicates (simple values)
const duped = [1, 2, 2, 3, 3, 3, 4];
const unique = duped.filter((val, idx, arr) => arr.indexOf(val) === idx);
// [1, 2, 3, 4]
// (For large arrays, use new Set([...duped]) instead)

reduce — Accumulate Into One Value

The most powerful array method. Reduces an array to a single value by applying a function against an accumulator.

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

// Sum
const sum = numbers.reduce((acc, n) => acc + n, 0);
// 15

// Max value
const max = numbers.reduce((acc, n) => (n > acc ? n : acc), -Infinity);
// 5

// Count occurrences
const fruits = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple'];

const counts = fruits.reduce((acc, fruit) => {
  acc[fruit] = (acc[fruit] || 0) + 1;
  return acc;
}, {});
// { apple: 3, banana: 2, cherry: 1 }

// Group by property
const people = [
  { name: 'Alice', dept: 'Engineering' },
  { name: 'Bob', dept: 'Marketing' },
  { name: 'Carol', dept: 'Engineering' },
  { name: 'Dave', dept: 'Marketing' },
];

const byDept = people.reduce((acc, person) => {
  const key = person.dept;
  if (!acc[key]) acc[key] = [];
  acc[key].push(person);
  return acc;
}, {});
// {
//   Engineering: [{ name: 'Alice'... }, { name: 'Carol'... }],
//   Marketing: [{ name: 'Bob'... }, { name: 'Dave'... }]
// }

Note: ES2024 introduced Object.groupBy() which handles the grouping pattern more cleanly:

const byDept = Object.groupBy(people, (person) => person.dept);

Always provide an initial value (second argument to reduce). Without it, the first element becomes the initial accumulator, which often causes bugs:

// BAD — no initial value, fails on empty array
[].reduce((acc, n) => acc + n);
// TypeError: Reduce of empty array with no initial value

// GOOD — always provide initial value
[].reduce((acc, n) => acc + n, 0);
// 0

flatMap — Map + Flatten in One Step

Maps each element, then flattens the result by one level. Perfect for operations that return arrays per element.

const sentences = ['Hello world', 'Goodbye moon'];

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

// Without flatMap:
const words2 = sentences.map((s) => s.split(' ')).flat();
// Same result, but two iterations instead of one

// Filter and transform in one pass
const nums = [1, 2, 3, 4, 5, 6];

const doubledEvens = nums.flatMap((n) => (n % 2 === 0 ? [n * 2] : []));
// [4, 8, 12] — even numbers doubled, odds removed

// Expand nested data
const orders = [
  { id: 1, items: ['apple', 'banana'] },
  { id: 2, items: ['cherry'] },
];

const allItems = orders.flatMap((order) =>
  order.items.map((item) => ({ orderId: order.id, item }))
);
// [
//   { orderId: 1, item: 'apple' },
//   { orderId: 1, item: 'banana' },
//   { orderId: 2, item: 'cherry' }
// ]

Search and Test Methods

find and findIndex

find returns the first element that matches. findIndex returns its index.

const users = [
  { id: 1, name: 'Alice', role: 'admin' },
  { id: 2, name: 'Bob', role: 'user' },
  { id: 3, name: 'Carol', role: 'user' },
];

const admin = users.find((u) => u.role === 'admin');
// { id: 1, name: 'Alice', role: 'admin' }

const idx = users.findIndex((u) => u.name === 'Bob');
// 1

const missing = users.find((u) => u.name === 'Dave');
// undefined

const missingIdx = users.findIndex((u) => u.name === 'Dave');
// -1

ES2023 added findLast and findLastIndex for searching from the end:

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

numbers.findLast((n) => n > 2);      // 3 (the second 3)
numbers.findLastIndex((n) => n > 2); // 4

some and every

some returns true if at least one element passes. every returns true if all elements pass.

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

numbers.some((n) => n > 4);   // true — 5 is > 4
numbers.some((n) => n > 10);  // false — none > 10

numbers.every((n) => n > 0);  // true — all positive
numbers.every((n) => n > 3);  // false — 1 and 2 fail

// Practical: check if user has required permissions
const userPerms = ['read', 'write', 'delete'];
const required = ['read', 'write'];

const hasAccess = required.every((perm) => userPerms.includes(perm));
// true

Both short-circuit — some stops at the first true, every stops at the first false.

includes and indexOf

Simple value lookups (no callback):

const fruits = ['apple', 'banana', 'cherry'];

fruits.includes('banana');  // true
fruits.includes('grape');   // false

fruits.indexOf('banana');   // 1
fruits.indexOf('grape');    // -1

// includes handles NaN correctly, indexOf does not
[1, NaN, 3].includes(NaN);  // true
[1, NaN, 3].indexOf(NaN);   // -1 (NaN !== NaN)

Sorting

sort — Mutates the Original

sort sorts in place and returns the same array. This is one of the most common gotchas.

const nums = [3, 1, 4, 1, 5, 9, 2, 6];

nums.sort();
// [1, 1, 2, 3, 4, 5, 6, 9]

// BUT: default sort converts to strings!
const big = [10, 9, 80, 100, 3];
big.sort();
// [10, 100, 3, 80, 9] — WRONG! Sorted as strings ("10" < "100" < "3")

// Always provide a comparator for numbers
big.sort((a, b) => a - b);   // ascending: [3, 9, 10, 80, 100]
big.sort((a, b) => b - a);   // descending: [100, 80, 10, 9, 3]

// Sort objects by property
const users = [
  { name: 'Charlie', age: 35 },
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
];

users.sort((a, b) => a.age - b.age);
// [Alice(25), Bob(30), Charlie(35)]

// String sorting with locale awareness
const words = ['banana', 'Apple', 'cherry'];
words.sort((a, b) => a.localeCompare(b));
// ['Apple', 'banana', 'cherry']

Critical: sort mutates the original array:

const original = [3, 1, 2];
const sorted = original.sort();

console.log(original); // [1, 2, 3] — MUTATED
console.log(sorted);   // [1, 2, 3]
console.log(original === sorted); // true — same reference!

// To sort without mutating, use toSorted() (ES2023) or spread:
const safelySorted = [...original].sort();
// or
const safelySorted2 = original.toSorted((a, b) => a - b);

toSorted, toReversed, toSpliced (ES2023)

Immutable versions that return new arrays:

const arr = [3, 1, 2];

const sorted = arr.toSorted((a, b) => a - b);
// sorted: [1, 2, 3]
// arr: [3, 1, 2] — unchanged

const reversed = arr.toReversed();
// reversed: [2, 1, 3]
// arr: [3, 1, 2] — unchanged

const spliced = arr.toSpliced(1, 1, 99);
// spliced: [3, 99, 2]
// arr: [3, 1, 2] — unchanged
┌───────────────────┬────────────────────┬──────────────┐
│ Mutating Method   │ Immutable Version  │ ES Version   │
├───────────────────┼────────────────────┼──────────────┤
│ sort()            │ toSorted()         │ ES2023       │
│ reverse()         │ toReversed()       │ ES2023       │
│ splice()          │ toSpliced()        │ ES2023       │
│ arr[i] = val      │ with(index, val)   │ ES2023       │
└───────────────────┴────────────────────┴──────────────┘

Flattening

flat — Flatten Nested Arrays

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); // flattens completely, any depth

// Practical: API returns inconsistent data
const responses = [
  [{ id: 1 }, { id: 2 }],
  [{ id: 3 }],
  [],
  [{ id: 4 }, { id: 5 }],
];

const allItems = responses.flat();
// [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }]

Iteration Methods

forEach — Side Effects Only

Executes a function for each element. Returns undefined — cannot chain.

const users = ['Alice', 'Bob', 'Carol'];

users.forEach((user, index) => {
  console.log(`${index + 1}. ${user}`);
});
// 1. Alice
// 2. Bob
// 3. Carol

You cannot break out of forEach. Use for...of or for if you need to break or return:

// BAD — return doesn't stop forEach, it just skips to next iteration
['a', 'b', 'c'].forEach((item) => {
  if (item === 'b') return; // only skips 'b', doesn't stop loop
  console.log(item);
});
// a, c

// GOOD — use for...of to break
for (const item of ['a', 'b', 'c']) {
  if (item === 'b') break;
  console.log(item);
}
// a

entries, keys, values — Iterators

const fruits = ['apple', 'banana', 'cherry'];

for (const [index, fruit] of fruits.entries()) {
  console.log(`${index}: ${fruit}`);
}
// 0: apple
// 1: banana
// 2: cherry

[...fruits.keys()];    // [0, 1, 2]
[...fruits.values()];  // ['apple', 'banana', 'cherry']

Adding and Removing Elements

const arr = [1, 2, 3];

// End
arr.push(4);       // [1, 2, 3, 4] — returns new length (4)
arr.pop();         // [1, 2, 3] — returns removed element (4)

// Beginning
arr.unshift(0);    // [0, 1, 2, 3] — returns new length (4)
arr.shift();       // [1, 2, 3] — returns removed element (0)

// Middle (splice: start, deleteCount, ...items)
arr.splice(1, 1);         // removes 1 element at index 1 → [1, 3]
arr.splice(1, 0, 2);      // inserts 2 at index 1 → [1, 2, 3]
arr.splice(1, 1, 20, 30); // replaces index 1 → [1, 20, 30, 3]
┌──────────┬──────────────┬─────────────┬────────────┐
│ Method   │ Position     │ Mutates?    │ Returns    │
├──────────┼──────────────┼─────────────┼────────────┤
│ push     │ End          │ Yes         │ New length │
│ pop      │ End          │ Yes         │ Removed el │
│ unshift  │ Start        │ Yes         │ New length │
│ shift    │ Start        │ Yes         │ Removed el │
│ splice   │ Any position │ Yes         │ Removed [] │
│ concat   │ End          │ No (new)    │ New array  │
│ slice    │ Range        │ No (new)    │ New array  │
└──────────┴──────────────┴─────────────┴────────────┘

slice — Extract Without Mutating

const arr = [0, 1, 2, 3, 4, 5];

arr.slice(2);      // [2, 3, 4, 5] — from index 2 to end
arr.slice(1, 4);   // [1, 2, 3] — from index 1 to 3 (exclusive end)
arr.slice(-2);     // [4, 5] — last 2 elements
arr.slice();       // [0, 1, 2, 3, 4, 5] — shallow copy

Creating Arrays

// Array.from — convert iterable or array-like to array
Array.from('hello');                    // ['h', 'e', 'l', 'l', 'o']
Array.from({ length: 5 }, (_, i) => i); // [0, 1, 2, 3, 4]
Array.from(document.querySelectorAll('div')); // NodeList to array

// Array.of — create array from arguments (unlike Array constructor)
Array.of(3);        // [3]
Array(3);           // [undefined, undefined, undefined] (confusing)

// fill — fill with a value
new Array(5).fill(0);        // [0, 0, 0, 0, 0]
new Array(3).fill({ x: 1 }); // [{x:1}, {x:1}, {x:1}] — same reference!

// For unique objects per element:
Array.from({ length: 3 }, () => ({ x: 1 }));
// [{x:1}, {x:1}, {x:1}] — different references

Functional Programming Patterns

Chaining Methods

const orders = [
  { id: 1, total: 250, status: 'completed', customer: 'Alice' },
  { id: 2, total: 50, status: 'pending', customer: 'Bob' },
  { id: 3, total: 175, status: 'completed', customer: 'Carol' },
  { id: 4, total: 400, status: 'completed', customer: 'Alice' },
  { id: 5, total: 30, status: 'cancelled', customer: 'Dave' },
];

// Get total revenue from completed orders over $100
const revenue = orders
  .filter((o) => o.status === 'completed')
  .filter((o) => o.total > 100)
  .reduce((sum, o) => sum + o.total, 0);
// 825

// Get formatted list of completed order customers
const customers = orders
  .filter((o) => o.status === 'completed')
  .map((o) => o.customer)
  .filter((name, i, arr) => arr.indexOf(name) === i) // unique
  .sort()
  .join(', ');
// 'Alice, Carol'

Pipe Pattern

For long chains, a pipe function can improve readability:

const pipe = (...fns) => (value) => fns.reduce((acc, fn) => fn(acc), value);

const processOrders = pipe(
  (orders) => orders.filter((o) => o.status === 'completed'),
  (orders) => orders.filter((o) => o.total > 100),
  (orders) => orders.map((o) => ({ customer: o.customer, total: o.total })),
  (orders) => orders.sort((a, b) => b.total - a.total)
);

const result = processOrders(orders);

Replace Imperative Loops

// Imperative
const results = [];
for (let i = 0; i < items.length; i++) {
  if (items[i].active) {
    results.push(items[i].name.toUpperCase());
  }
}

// Functional — same result, more declarative
const results = items
  .filter((item) => item.active)
  .map((item) => item.name.toUpperCase());

Reducing into Different Shapes

// Array → Object (index by ID)
const users = [
  { id: 'a1', name: 'Alice' },
  { id: 'b2', name: 'Bob' },
];

const byId = users.reduce((acc, user) => {
  acc[user.id] = user;
  return acc;
}, {});
// { a1: { id: 'a1', name: 'Alice' }, b2: { id: 'b2', name: 'Bob' } }

// Array → Map
const userMap = new Map(users.map((u) => [u.id, u]));

// Flatten + transform in one reduce
const nested = [[1, 2], [3, 4], [5, 6]];
const flatDoubled = nested.reduce((acc, arr) => {
  return acc.concat(arr.map((n) => n * 2));
}, []);
// [2, 4, 6, 8, 10, 12]

Performance Considerations

When to Use Which Method

┌────────────────┬────────────────────┬──────────────────────────┐
│ Task           │ Best Method        │ Why                      │
├────────────────┼────────────────────┼──────────────────────────┤
│ Transform all  │ map                │ Returns new array        │
│ Keep subset    │ filter             │ Returns matching items   │
│ Find one       │ find               │ Stops at first match     │
│ Check existence│ some / includes    │ Short-circuits           │
│ Aggregate      │ reduce             │ Single pass              │
│ Side effects   │ forEach / for...of │ No return value needed   │
│ Break early    │ for...of / for     │ forEach can't break      │
│ Best perf      │ for loop           │ No function call overhead│
└────────────────┴────────────────────┴──────────────────────────┘

Avoid Unnecessary Iterations

// BAD — 3 iterations over the array
const result = items
  .map((item) => transform(item))
  .filter((item) => item.valid)
  .map((item) => format(item));

// BETTER — 1 iteration with reduce
const result = items.reduce((acc, item) => {
  const transformed = transform(item);
  if (transformed.valid) {
    acc.push(format(transformed));
  }
  return acc;
}, []);

// ALSO GOOD — flatMap for filter+map in one pass
const result = items.flatMap((item) => {
  const transformed = transform(item);
  return transformed.valid ? [format(transformed)] : [];
});

Large Arrays — Use for Loops

For performance-critical code on very large arrays (100K+ elements), plain for loops are faster than array methods due to function call overhead:

// Array method — cleaner but slower on huge arrays
const sum = largeArray.reduce((acc, n) => acc + n, 0);

// For loop — verbose but faster for 100K+ elements
let sum = 0;
for (let i = 0; i < largeArray.length; i++) {
  sum += largeArray[i];
}

In practice, the difference is negligible for arrays under ~10,000 elements. Prefer readability over micro-optimization.

Set for Membership Checks

If you're calling includes() in a loop, convert to a Set first:

const allowedIds = ['a1', 'b2', 'c3', 'd4', 'e5'];

// BAD — O(n) lookup on every iteration = O(n*m) total
const filtered = items.filter((item) => allowedIds.includes(item.id));

// GOOD — O(1) lookup on every iteration = O(n+m) total
const allowedSet = new Set(allowedIds);
const filtered = items.filter((item) => allowedSet.has(item.id));

Common Recipes

Remove Duplicates

// Simple values
const unique = [...new Set([1, 2, 2, 3, 3, 3])];
// [1, 2, 3]

// Objects by property
function uniqueBy(arr, key) {
  const seen = new Set();
  return arr.filter((item) => {
    const val = item[key];
    if (seen.has(val)) return false;
    seen.add(val);
    return true;
  });
}

uniqueBy(users, 'email');

Chunk an Array

function chunk(arr, size) {
  return Array.from({ length: Math.ceil(arr.length / size) }, (_, i) =>
    arr.slice(i * size, i * size + size)
  );
}

chunk([1, 2, 3, 4, 5, 6, 7], 3);
// [[1, 2, 3], [4, 5, 6], [7]]

Intersection and Difference

function intersection(a, b) {
  const setB = new Set(b);
  return a.filter((item) => setB.has(item));
}

function difference(a, b) {
  const setB = new Set(b);
  return a.filter((item) => !setB.has(item));
}

intersection([1, 2, 3, 4], [3, 4, 5, 6]); // [3, 4]
difference([1, 2, 3, 4], [3, 4, 5, 6]);   // [1, 2]

Zip Two Arrays

function zip(a, b) {
  return a.map((val, i) => [val, b[i]]);
}

zip(['a', 'b', 'c'], [1, 2, 3]);
// [['a', 1], ['b', 2], ['c', 3]]

Key Takeaways

  1. map for transformation — returns a new array of the same length
  2. filter for subsetting — returns matching elements
  3. reduce for aggregation — accumulates into any shape (number, object, array)
  4. find for single lookup — returns first match, stops early
  5. some/every for boolean checks — short-circuit on first match/fail
  6. sort mutates — use toSorted() or spread+sort for immutability
  7. flatMap = map + flat — great for filter+transform in one step
  8. forEach cannot break — use for...of when you need to stop early
  9. Always pass a comparator to sort — default sort converts to strings
  10. Set for fast lookups — convert arrays to Sets when checking membership in loops

Found this helpful?

Support devsofus — help us keep creating free dev guides.

Related Articles