Find things, test things, sort things (carefully!) and flatten nested arrays — plus the ES2023 non-mutating copies.
Finding and testing
| Method | Returns | Stops early? |
|---|---|---|
find(fn) / findLast(fn) | First / last matching element or undefined | Yes |
findIndex(fn) / findLastIndex(fn) | Index or -1 | Yes |
indexOf(x) / lastIndexOf(x) | Index via === or -1 | Yes |
includes(x) | Boolean (finds NaN) | Yes |
some(fn) | true if any passes | Yes, on first true |
every(fn) | true if all pass (true for []) | Yes, on first false |
const users = [
{ id: 1, name: "Asha", admin: false },
{ id: 2, name: "Ravi", admin: true },
{ id: 3, name: "Mei", admin: true },
];
console.log(users.find((u) => u.admin).name, users.findLast((u) => u.admin).name);
console.log(users.findIndex((u) => u.id === 3), users.find((u) => u.id === 9));
console.log(users.some((u) => u.admin), users.every((u) => u.admin));
console.log([].every((x) => x > 100), [].some((x) => x > 100));
console.log([1, 2, NaN].includes(NaN), [1, 2, NaN].indexOf(NaN));Ravi Mei
2 undefined
true false
true false
true -1Sorting — the big pitfall
const nums = [10, 1, 5, 100, 25];
console.log([...nums].sort()); // compares as STRINGS
console.log([...nums].sort((a, b) => a - b)); // numeric ascending
console.log([...nums].sort((a, b) => b - a)); // descending[ 1, 10, 100, 25, 5 ]
[ 1, 5, 10, 25, 100 ]
[ 100, 25, 10, 5, 1 ]The comparator returns a negative number if a goes first, positive if b goes first, 0 to keep order. Since ES2019 the sort is stable, so equal items keep their relative order — which lets you sort by several keys.
const people = [
{ name: "Zoe", dept: "HR", age: 30 },
{ name: "adam", dept: "Eng", age: 25 },
{ name: "Émile", dept: "Eng", age: 35 },
{ name: "Bob", dept: "HR", age: 25 },
];
const byDeptThenAge = people.toSorted(
(a, b) => a.dept.localeCompare(b.dept) || a.age - b.age
);
console.log(byDeptThenAge.map((p) => `${p.dept}:${p.name}`).join(" "));
const names = people.map((p) => p.name);
console.log(names.toSorted());
console.log(names.toSorted((a, b) => a.localeCompare(b)));
console.log(people[0].name); // original untouched by toSortedEng:adam Eng:Émile HR:Bob HR:Zoe
[ 'Bob', 'Zoe', 'adam', 'Émile' ]
[ 'adam', 'Bob', 'Émile', 'Zoe' ]
ZoeMutating vs copying (ES2023)
| Mutates | Non-mutating copy |
|---|---|
sort(fn) | toSorted(fn) |
reverse() | toReversed() |
splice(i, n, …) | toSpliced(i, n, …) |
arr[i] = v | arr.with(i, v) |
fill, copyWithin, push, pop, shift, unshift | spread / slice / concat |
Flattening
const nested = [1, [2, [3, [4, [5]]]]];
console.log(nested.flat());
console.log(nested.flat(2));
console.log(nested.flat(Infinity));[ 1, 2, [ 3, [ 4, [Array] ] ] ]
[ 1, 2, 3, [ 4, [ 5 ] ] ]
[ 1, 2, 3, 4, 5 ]Useful one-liners
const arr = [3, 1, 3, 2, 1];
console.log([...new Set(arr)]); // unique
console.log(Math.max(...arr), arr.reduce((a, b) => a + b, 0)); // max, sum
const chunk = (a, n) => Array.from({ length: Math.ceil(a.length / n) }, (_, i) => a.slice(i * n, i * n + n));
console.log(chunk([1, 2, 3, 4, 5], 2));
const zip = (a, b) => a.map((x, i) => [x, b[i]]);
console.log(zip(["a", "b"], [1, 2]));
console.log([1, 2, 3, 4].filter((x) => [2, 4, 6].includes(x))); // intersection[ 3, 1, 2 ]
3 10
[ [ 1, 2 ], [ 3, 4 ], [ 5 ] ]
[ [ 'a', 1 ], [ 'b', 2 ] ]
[ 2, 4 ]Why does [10, 1, 5].sort() return [1, 10, 5]?
find vs filter?