A practical guide to modern JavaScript array methods — from everyday basics like map and filter to newer additions like groupBy, toSorted, and findLast.
ES2023 introduced immutable versions of array methods that return new arrays instead of modifying the original. This eliminates an entire class of bugs:
const original = [3, 1, 4, 1, 5];
// Old (mutates original — dangerous!)
original.sort(); // original is now [1, 1, 3, 4, 5]
// New (returns a new sorted array)
const sorted = original.toSorted();
// original: [3, 1, 4, 1, 5] (unchanged)
// sorted: [1, 1, 3, 4, 5]
// Similarly:
const reversed = original.toReversed();
const spliced = original.toSpliced(1, 1); // Remove index 1
const changed = original.with(0, 99); // Replace index 0
Always prefer these over their mutating counterparts. They prevent bugs in React state updates, shared data structures, and functional pipelines.Previously you needed lodash's groupBy or wrote it yourself. Now it's built-in:
const users = [
{ name: 'Alice', role: 'admin' },
{ name: 'Bob', role: 'user' },
{ name: 'Charlie', role: 'admin' },
{ name: 'Diana', role: 'user' },
];
const grouped = Object.groupBy(users, user => user.role);
// {
// admin: [{ name: 'Alice', role: 'admin' }, { name: 'Charlie', role: 'admin' }],
// user: [{ name: 'Bob', role: 'user' }, { name: 'Diana', role: 'user' }]
// }
Real-world use cases:
// Group orders by status
const byStatus = Object.groupBy(orders, o => o.status);
// Group files by extension
const byType = Object.groupBy(files, f => f.name.split('.').pop());
// Group events by date
const byDate = Object.groupBy(events, e => e.date.toDateString());
Supported in all modern browsers and Node.js 21+.
Search arrays from the end — useful when the last match is what you need:
const transactions = [
{ id: 1, type: 'credit', amount: 100 },
{ id: 2, type: 'debit', amount: 50 },
{ id: 3, type: 'credit', amount: 200 },
{ id: 4, type: 'debit', amount: 75 },
];
// Find the most recent credit
const lastCredit = transactions.findLast(t => t.type === 'credit');
// { id: 3, type: 'credit', amount: 200 }
// Find its index
const lastCreditIndex = transactions.findLastIndex(t => t.type === 'credit');
// 2
Before findLast, you had to reverse the array first or loop manually. This is cleaner and doesn't create a copy of the array.
The real power of array methods is chaining them for complex data transformations:
// Transform API response into UI-ready data
const tableData = rawUsers
.filter(user => user.status === 'active')
.toSorted((a, b) => b.lastLogin - a.lastLogin)
.slice(0, 10)
.map(user => ({
name: user.fullName,
email: user.email,
lastSeen: formatDate(user.lastLogin),
plan: user.plan.toUpperCase(),
}));
// Calculate statistics
const stats = orders
.filter(o => o.date >= startOfMonth)
.reduce((acc, order) => {
acc.total += order.amount;
acc.count += 1;
acc.avg = acc.total / acc.count;
return acc;
}, { total: 0, count: 0, avg: 0 });
// Flatten nested arrays
const allTags = posts
.flatMap(post => post.tags)
.filter((tag, i, arr) => arr.indexOf(tag) === i); // unique
Tip: For uniqueness, use [...new Set(array)] instead of the filter trick — it's faster and cleaner.Method chaining creates intermediate arrays. For small arrays (< 10,000 items) this doesn't matter. For large arrays, consider:
// Bad: creates 3 intermediate arrays
const result = hugeArray
.filter(x => x.active)
.map(x => x.value)
.reduce((sum, v) => sum + v, 0);
// Better: single pass
let sum = 0;
for (const x of hugeArray) {
if (x.active) sum += x.value;
}
Rules of thumb:
reduce or for...of loop