Transforming: map
map() creates a new array by calling a function on every element. It does not mutate the original array. The returned array always has the same length as the original.
const numbers = [1, 2, 3, 4, 5];
// Double each number
const doubled = numbers.map(n => n * 2);
// [2, 4, 6, 8, 10]
// Extract a property from objects
const users = [
{ name: "Alice", age: 30 },
{ name: "Bob", age: 25 },
{ name: "Charlie", age: 35 }
];
const names = users.map(user => user.name);
// ["Alice", "Bob", "Charlie"]
// Transform with index
const indexed = ["a", "b", "c"].map((letter, i) => `${i}: ${letter}`);
// ["0: a", "1: b", "2: c"]
// Parse strings to numbers
const strings = ["1", "2", "3"];
const nums = strings.map(Number);
// [1, 2, 3]
When working with complex transformations, you can format and minify your JavaScript with the JavaScript Minifier for production-ready output.
Filtering: filter
filter() creates a new array containing only elements that pass a test. The callback must return true to keep the element or false to exclude it.
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Even numbers only
const evens = numbers.filter(n => n % 2 === 0);
// [2, 4, 6, 8, 10]
// Filter objects by property
const users = [
{ name: "Alice", active: true },
{ name: "Bob", active: false },
{ name: "Charlie", active: true }
];
const activeUsers = users.filter(u => u.active);
// [{ name: "Alice", active: true }, { name: "Charlie", active: true }]
// Remove falsy values (null, undefined, 0, "", false)
const messy = [0, 1, null, "hello", undefined, "", false, 42];
const clean = messy.filter(Boolean);
// [1, "hello", 42]
// Remove duplicates (simple values)
const dupes = [1, 2, 2, 3, 3, 3, 4];
const unique = dupes.filter((val, i, arr) => arr.indexOf(val) === i);
// [1, 2, 3, 4]
// Better: [...new Set(dupes)]
Accumulating: reduce
reduce() processes every element and accumulates a single result. It is the most powerful and flexible array method, capable of implementing map, filter, and nearly any other array operation.
const numbers = [1, 2, 3, 4, 5];
// Sum all numbers
const sum = numbers.reduce((acc, n) => acc + n, 0);
// 15
// Find maximum value
const max = numbers.reduce((a, b) => a > b ? a : b);
// 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: "Charlie", dept: "Engineering" }
];
const byDept = people.reduce((acc, person) => {
const key = person.dept;
(acc[key] = acc[key] || []).push(person);
return acc;
}, {});
// Flatten nested arrays
const nested = [[1, 2], [3, 4], [5, 6]];
const flat = nested.reduce((acc, arr) => acc.concat(arr), []);
// [1, 2, 3, 4, 5, 6]
// Better: nested.flat()
Calling reduce() on an empty array without an initial value throws a TypeError. Always pass an initial value as the second argument: arr.reduce(fn, initialValue). This also makes the code more readable because the return type is obvious from the initial value.
Iterating: forEach
forEach() executes a function on every element but returns undefined. Use it for side effects, not for creating new arrays.
const items = ["apple", "banana", "cherry"];
// Log each item
items.forEach(item => console.log(item));
// Update DOM elements
document.querySelectorAll(".card").forEach(card => {
card.classList.add("visible");
});
// forEach with index
items.forEach((item, index) => {
console.log(`${index + 1}. ${item}`);
});
Use map() when you need the returned array. Use forEach() when you are performing side effects (logging, DOM updates, API calls). Never use map() and ignore the result — that creates an unnecessary array in memory.
Searching: find, findIndex, indexOf, includes
const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Charlie" }
];
// find: returns first matching element (or undefined)
const bob = users.find(u => u.name === "Bob");
// { id: 2, name: "Bob" }
// findIndex: returns index of first match (or -1)
const bobIndex = users.findIndex(u => u.name === "Bob");
// 1
// findLast / findLastIndex (ES2023): search from the end
const numbers = [1, 2, 3, 2, 1];
const lastTwo = numbers.findLast(n => n === 2); // 2 (at index 3)
const lastTwoIdx = numbers.findLastIndex(n => n === 2); // 3
// indexOf: find index of a primitive value
const colors = ["red", "green", "blue"];
colors.indexOf("green"); // 1
colors.indexOf("purple"); // -1
// includes: check if a value exists (returns boolean)
colors.includes("blue"); // true
colors.includes("yellow"); // false
Testing: some, every
const numbers = [2, 4, 6, 8, 10];
// every: do ALL elements pass the test?
const allEven = numbers.every(n => n % 2 === 0); // true
// some: does ANY element pass the test?
const hasLarge = numbers.some(n => n > 7); // true
// Practical: check if form is valid
const fields = [
{ name: "email", valid: true },
{ name: "password", valid: false }
];
const formValid = fields.every(f => f.valid); // false
// Practical: check if any task is overdue
const tasks = [
{ title: "Deploy", due: "2026-02-20" },
{ title: "Test", due: "2026-03-01" }
];
const hasOverdue = tasks.some(t => new Date(t.due) < new Date());
Sorting: sort, toSorted, reverse, toReversed
// CAUTION: sort() MUTATES the original array
const nums = [10, 1, 21, 2];
nums.sort(); // [1, 10, 2, 21] -- lexicographic, NOT numeric!
// Numeric sort (ascending)
[10, 1, 21, 2].sort((a, b) => a - b); // [1, 2, 10, 21]
// Numeric sort (descending)
[10, 1, 21, 2].sort((a, b) => b - a); // [21, 10, 2, 1]
// Sort objects by property
const users = [
{ name: "Charlie", age: 35 },
{ name: "Alice", age: 30 },
{ name: "Bob", age: 25 }
];
users.sort((a, b) => a.age - b.age);
// Sort strings (locale-aware)
["charlie", "Alice", "bob"].sort((a, b) => a.localeCompare(b));
// NON-MUTATING alternatives (ES2023)
const original = [3, 1, 2];
const sortedCopy = original.toSorted((a, b) => a - b);
// original: [3, 1, 2] (unchanged)
// sortedCopy: [1, 2, 3]
const reversed = original.toReversed();
// original: [3, 1, 2] (unchanged)
// reversed: [2, 1, 3]
Adding and Removing: push, pop, shift, unshift, splice
These methods mutate the original array. For non-mutating alternatives, use spread syntax or toSpliced().
const arr = [1, 2, 3];
arr.push(4); // Add to end. arr = [1, 2, 3, 4]
arr.pop(); // Remove from end. Returns 4
arr.unshift(0); // Add to beginning. arr = [0, 1, 2, 3]
arr.shift(); // Remove from beginning. Returns 0
// splice: remove/insert at any position
arr.splice(1, 1); // Remove 1 element at index 1
arr.splice(1, 0, 2); // Insert 2 at index 1
arr.splice(1, 1, 20, 30); // Replace 1 element with two
// toSpliced (ES2023): non-mutating splice
const original = [1, 2, 3, 4];
const modified = original.toSpliced(1, 2, 20, 30);
// original: [1, 2, 3, 4] (unchanged)
// modified: [1, 20, 30, 4]
Combining: concat, flat, flatMap, spread
// concat: merge arrays (non-mutating)
const merged = [1, 2].concat([3, 4]); // [1, 2, 3, 4]
const merged2 = [...[1, 2], ...[3, 4]]; // Same with spread
// flat: flatten nested arrays
[[1, 2], [3, [4, 5]]].flat(); // [1, 2, 3, [4, 5]]
[[1, 2], [3, [4, 5]]].flat(2); // [1, 2, 3, 4, 5]
nested.flat(Infinity); // Flatten all levels
// flatMap: map then flatten one level (single pass)
const sentences = ["Hello world", "foo bar"];
const words = sentences.flatMap(s => s.split(" "));
// ["Hello", "world", "foo", "bar"]
// Practical flatMap: expand items
const orders = [
{ id: 1, items: ["apple", "banana"] },
{ id: 2, items: ["cherry"] }
];
const allItems = orders.flatMap(o => o.items);
// ["apple", "banana", "cherry"]
Creating: from, of, Array constructor, fill
// Array.from: convert iterable/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"));
// Array.of: create array from arguments
Array.of(1, 2, 3); // [1, 2, 3]
Array.of(5); // [5] (not [undefined x 5])
// fill: fill array with a value
new Array(5).fill(0); // [0, 0, 0, 0, 0]
[1, 2, 3, 4].fill(0, 1, 3); // [1, 0, 0, 4]
// with (ES2023): replace one element (non-mutating)
const arr = [1, 2, 3, 4];
const updated = arr.with(2, 99);
// arr: [1, 2, 3, 4] (unchanged)
// updated: [1, 2, 99, 4]
ES2023+ New Methods
Recent ECMAScript versions added non-mutating alternatives and new utility methods supported in all modern browsers and Node.js 20+.
const inventory = [
{ name: "asparagus", type: "vegetables" },
{ name: "bananas", type: "fruit" },
{ name: "cherries", type: "fruit" },
{ name: "goat", type: "meat" }
];
const grouped = Object.groupBy(inventory, item => item.type);
// {
// vegetables: [{ name: "asparagus", ... }],
// fruit: [{ name: "bananas", ... }, { name: "cherries", ... }],
// meat: [{ name: "goat", ... }]
// }
Mutates: push, pop, shift, unshift, splice, sort, reverse, fill. Does not mutate: map, filter, reduce, concat, flat, flatMap, slice, toSorted, toReversed, toSpliced, with. When in doubt, prefer non-mutating methods to avoid side-effect bugs.
Method Chaining Patterns
Because map, filter, and other methods return arrays, you can chain them into data processing pipelines:
const transactions = [
{ id: 1, amount: 50, type: "income" },
{ id: 2, amount: 30, type: "expense" },
{ id: 3, amount: 100, type: "income" },
{ id: 4, amount: 20, type: "expense" },
{ id: 5, amount: 200, type: "income" }
];
// Pipeline: filter -> map -> reduce
const totalIncome = transactions
.filter(t => t.type === "income")
.map(t => t.amount)
.reduce((sum, amount) => sum + amount, 0);
// 350
// Get sorted, formatted list of expensive items
const products = [
{ name: "Laptop", price: 999 },
{ name: "Mouse", price: 29 },
{ name: "Monitor", price: 499 },
{ name: "Keyboard", price: 79 }
];
const expensiveFormatted = products
.filter(p => p.price > 50)
.toSorted((a, b) => b.price - a.price)
.map(p => `${p.name}: $${p.price}`);
// ["Laptop: $999", "Monitor: $499", "Keyboard: $79"]
Performance Considerations
- Chaining creates intermediate arrays.
arr.filter().map()iterates twice and creates two arrays. For very large arrays (100k+ elements), consider a singlereduce()or aforloop. - for loops are faster for pure computation on large datasets. The difference is negligible for arrays under 10,000 elements.
- find() and some() stop early. They return as soon as they find a match, making them efficient for large arrays.
- Avoid creating arrays you do not need. Use
forEach()for side effects, notmap(). Usesome()instead offilter().length > 0. - Set has O(1) lookup. If you call
includes()repeatedly on the same array, convert it to aSetfirst.
Related Free Tools
Frequently Asked Questions
map() creates and returns a new array with the results of calling a function on every element. forEach() executes a function on every element but returns undefined. Use map() when you need to transform data and use the resulting array. Use forEach() when you want to perform side effects like logging, updating the DOM, or making API calls for each element. Because map() returns a new array, you can chain it with other array methods like filter() and reduce(). forEach() cannot be chained since it returns undefined.
reduce() iterates over an array and accumulates a single result value. It takes two arguments: a callback function and an optional initial value. The callback receives four parameters: the accumulator (the running total or accumulated result), the current element, the current index, and the original array. On each iteration, the callback's return value becomes the new accumulator for the next iteration. Always provide an initial value to avoid unexpected behavior with empty arrays. Common uses include summing numbers (reduce((sum, n) => sum + n, 0)), flattening arrays, counting occurrences, and grouping objects by a property.
Yes, Array.sort() mutates the original array in place and also returns the sorted array. This is a common source of bugs. If you need to sort without modifying the original, create a copy first using the spread operator ([...arr].sort()), Array.from(arr).sort(), or arr.slice().sort(). As of ES2023, you can use the new toSorted() method which returns a sorted copy without mutating the original. Also note that sort() without a compare function converts elements to strings and sorts them lexicographically, which means [10, 9, 80].sort() produces [10, 80, 9]. Always pass a compare function for numbers: arr.sort((a, b) => a - b).
find() returns the first element that matches a condition, or undefined if no match is found. It stops searching as soon as it finds a match. filter() returns a new array containing all elements that match a condition, or an empty array if nothing matches. Use find() when you expect one result and want to stop early, like looking up a user by ID. Use filter() when you want all matching results, like getting all users in a specific city. find() is more efficient when you only need one result because it does not process the remaining elements after finding a match.
ES2023 introduced non-mutating versions of existing methods: toSorted() (like sort() but returns a new array), toReversed() (like reverse() but non-mutating), toSpliced() (like splice() but non-mutating), and with(index, value) which returns a new array with one element replaced. These are supported in all modern browsers. ES2024 added Object.groupBy(array, callback) which groups array elements by a key returned by the callback function, replacing the common reduce-based grouping pattern. It returns a null-prototype object with arrays as values.