Updated June 2026. Tested on modern JavaScript (ES2020+). Part of the Techalyst JavaScript series.
Most day-to-day JavaScript is moving data through arrays: transforming it, filtering it, summing it, reshaping it. A handful of methods cover almost all of that, and knowing which ones mutate (and which return a fresh array) saves you a class of sneaky bugs. Here is the working toolkit.
Transform and filter: map and filter
map runs a function over every element and returns a new array of the results. filter returns a new array of the elements that pass a test. Neither touches the original.
const names = products.map((p) => p.name)
const cheap = products.filter((p) => p.price < 100)
These two pair beautifully with arrow functions and chain naturally:
const labels = products
.filter((p) => p.inStock)
.map((p) => p.name.toUpperCase())
Search: find, findIndex, some, every
When you want an element rather than a whole array, reach for these.
const item = products.find((p) => p.id === 3) // the element, or undefined
const idx = products.findIndex((p) => p.id === 3) // its index, or -1
const hasDeal = products.some((p) => p.discount > 0) // true if ANY match
const allInStock = products.every((p) => p.stock > 0)// true if ALL match
findIndex plus splice is the standard "find then remove" move:
const i = products.findIndex((p) => p.id === 3)
if (i !== -1) products.splice(i, 1)
For a simple membership check, includes returns a boolean and reads cleaner than indexOf(...) !== -1.
reduce, and choosing the initial value
reduce collapses an array into a single value, an accumulator you build up element by element. The trick to using it well is picking the right initial value for what you are building.
// sum: start at 0
const total = products.reduce((sum, p) => sum + p.price, 0)
// collect: start at []
const expensive = products.reduce((list, p) => {
if (p.price > 100) list.push(p)
return list
}, [])
// count or group: start at {}
const byCategory = products.reduce((groups, p) => {
;(groups[p.category] ||= []).push(p)
return groups
}, {})
| Goal | Initial value |
|---|---|
| Sum a number | 0 |
| Collect into a list | [] |
| Count or group | {} |
Always pass the initial value explicitly. Omitting it makes the first element the starting accumulator, which is rarely what you want and breaks on an empty array.
Mutating versus non-mutating
This is the distinction that bites people. Some methods change the original array in place; others return a new one and leave the original alone.
// MUTATING, these change the array itself
arr.sort((a, b) => a - b) // also returns it, but sorts in place
arr.splice(2, 1) // removes 1 element at index 2
arr.reverse()
arr.push(x); arr.pop()
// NON-MUTATING, these return a new array
arr.slice(1, 3) // a portion, original untouched
arr.map(...); arr.filter(...)
const merged = [...a, ...b] // spread to combine
Two specifics worth pinning down. sort mutates and its comparator must return a number, not a boolean, return a - b for ascending, not a > b. And if you need to sort without disturbing the source (common in a Vue computed property), copy first: [...arr].sort(...). Modern engines also offer non-mutating twins like toSorted and toReversed if you can target them. For why this distinction matters so much inside a Vue app, see JavaScript array methods and Vue reactivity.
Reshaping objects
Arrays and objects convert into each other cleanly, which is how you sort or transform object data.
const obj = { Tokyo: 3, Paris: 2, Cairo: 5 }
Object.keys(obj) // ['Tokyo', 'Paris', 'Cairo']
Object.values(obj) // [3, 2, 5]
Object.entries(obj) // [['Tokyo', 3], ['Paris', 2], ['Cairo', 5]]
// sort an object by value, then rebuild it
const sorted = Object.fromEntries(
Object.entries(obj).sort((a, b) => b[1] - a[1]),
)
The entries to map/sort to fromEntries round trip is the standard way to transform an object while keeping it an object.
Wrapping up
Reach for map to transform, filter to narrow, find/some/every to search, and reduce to collapse (with the initial value matched to your goal). Keep the mutating set (sort, splice, reverse, push) straight from the non-mutating set (slice, map, spread), and copy before sorting when the source must stay intact. For object data, lean on Object.entries and Object.fromEntries to move between the two shapes.
More in the series: scope, hoisting and references and arrow functions and this. Questions welcome below.
All comments ()
No comments yet
Be the first to leave a comment on this post.