JavaScript cheat sheet
The ones worth learning first
| What | How |
|---|---|
| Transform a list | items.map(x => x.price * x.qty) |
| Keep some | items.filter(x => x.stock > 0) |
| Reduce to one value | items.reduce((sum, x) => sum + x.total, 0) |
| Find one | users.find(u => u.id === id) |
| Sort without mutating | [...arr].sort((a, b) => a.age - b.age) or arr.toSorted(...) |
| Group by | Object.groupBy(items, x => x.category) |
| Destructure | const { name, tags = [] } = user; const [first, ...rest] = list; |
| Spread and copy | const copy = { ...obj, status: 'done' }; |
| Optional chaining | user?.address?.city ?? 'unknown' |
| Await in parallel | const [a, b] = await Promise.all([fa(), fb()]); |
Arrays: transform
| map, filter, reduce | arr.map(fn) arr.filter(fn) arr.reduce((acc, x) => acc + x, 0) |
| Filter by a field | items.filter(i => i.stock > 0) |
| Map to strings | people.map(p => `${p.first} ${p.last}`) |
| every / some | tags.every(t => item.tags.includes(t)) arr.some(fn) |
| find / findIndex / findLast | arr.find(fn) arr.findIndex(fn) arr.findLast(fn) |
| includes / indexOf | arr.includes(x) arr.indexOf(x) |
| flat / flatMap | nested.flat() arr.flatMap(x => [x, x * 2]) |
| Sort numbers | arr.sort((a, b) => a - b) (default sort is lexicographic) |
| Sort by several keys | arr.sort((a, b) => a.dept.localeCompare(b.dept) || b.salary - a.salary) |
| Top n with ties | sorted.filter(x => x.score >= sorted[n - 1].score) |
| Non-mutating versions | toSorted, toReversed, toSpliced, with (ES2023) |
| Range | Array.from({ length: n }, (_, i) => i + start) |
| Chunk | for (let i = 0; i < arr.length; i += size) out.push(arr.slice(i, i + size)) |
| Zip | a.map((x, i) => [x, b[i]]) |
| Running totals | let s = 0; arr.map(x => (s += x)) |
| Unique | [...new Set(arr)] |
| Unique by a field | [...new Map(arr.map(x => [x.id, x])).values()] |
| Group by | Object.groupBy(arr, x => x.type) or arr.reduce((g, x) => ((g[x.type] ||= []).push(x), g), {}) |
| Count by | arr.reduce((c, x) => ((c[x.type] = (c[x.type] || 0) + 1), c), {}) |
| Totals per key | orders.reduce((t, o) => ((t[o.customer] = (t[o.customer] || 0) + o.total), t), {}) |
| Pivot to a lookup | Object.fromEntries(rows.map(r => [r.id, r])) |
| Left join | a.map(x => ({ ...x, ...(bById.get(x.id) || {}) })) |
| at | arr.at(-1) (last element) |
Objects
| Keys, values, entries | Object.keys(o) Object.values(o) Object.entries(o) |
| From entries | Object.fromEntries(entries) |
| Copy and override | { ...o, status: 'done' } |
| Pick keys | Object.fromEntries(keys.filter(k => k in o).map(k => [k, o[k]])) |
| Omit a key | const { password, ...safe } = user; |
| Deep merge | recursive: for each key, if both are plain objects merge, else take the override |
| Read a nested path | path.split('.').reduce((o, k) => o?.[k], obj) |
| Flatten nested | recurse with prefix: out[`${prefix}${k}`] = v |
| Deep equal | same type, same keys, recurse on values; arrays by index |
| Deep clone | structuredClone(obj) (not JSON.parse(JSON.stringify) which drops dates and undefined) |
| Freeze | Object.freeze(o) (shallow) |
| Optional chaining and nullish | o?.a?.b ?? fallback (?? only for null/undefined, || also for 0 and '') |
| Logical assignment | o.list ||= [] o.n ??= 0 o.flag &&= false |
| Computed key | { [key]: value } |
| Map and Set | new Map([[k, v]]) m.get(k) new Set(arr) s.has(x) |
Strings
| Template literal | `Hello ${name}, you have ${n} items` |
| Split, join, reverse words | s.split(' ').reverse().join(' ') |
| Capitalize each word | s.replace(/\b\w/g, c => c.toUpperCase()) |
| Count matches | (s.match(/[aeiou]/gi) || []).length |
| Trim, pad | s.trim() s.padStart(6, '0') s.padEnd(10) |
| Slugify | s.toLowerCase().trim().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') |
| Truncate with ellipsis | s.length > n ? s.slice(0, n - 1) + '…' : s |
| Format money | new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(n) |
| Format a number | n.toLocaleString('en-US', { maximumFractionDigits: 2 }) |
| Parse a query string | Object.fromEntries(new URLSearchParams(qs)) |
| Align columns | cells.map((c, i) => c.padEnd(widths[i])).join(' ') |
| CSV to objects | lines.slice(1).map(l => Object.fromEntries(l.split(',').map((v, i) => [header[i], v]))) |
| Starts, ends, includes | s.startsWith('a') s.endsWith('.js') s.includes('x') |
| replaceAll | s.replaceAll('-', ' ') or s.replace(/-/g, ' ') |
| Regex groups | const m = s.match(/(\d{4})-(\d{2})/); m[1] /(?<y>\d{4})/.exec(s).groups.y |
| Character code | 'A'.charCodeAt(0) String.fromCharCode(65) |
Numbers and dates
| Clamp | Math.min(Math.max(n, lo), hi) |
| Round to 2 decimals | Math.round(n * 100) / 100 (toFixed returns a string) |
| Parse | Number('42') parseInt('42px', 10) parseFloat('3.5kg') |
| Check | Number.isInteger(n) Number.isNaN(x) Number.isFinite(x) |
| Random integer | Math.floor(Math.random() * (max - min + 1)) + min |
| Now | new Date() Date.now() (ms) |
| Add days | const d = new Date(date); d.setDate(d.getDate() + n) |
| Days between | Math.round((b - a) / 86400000) |
| Last day of the month | new Date(y, m + 1, 0).getDate() |
| ISO date string | d.toISOString().slice(0, 10) |
| Format a date | new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' }).format(d) |
| Format a duration | `${Math.floor(s / 3600)}h ${Math.floor(s % 3600 / 60)}m` |
| Month index gotcha | getMonth() is 0-based; new Date('2026-01-05') is UTC midnight. |
Functions and async
| Arrow function | const add = (a, b) => a + b; const f = x => ({ x }); (parentheses for an object) |
| Default and rest params | function f(a, b = 2, ...rest) {} |
| Closure counter | const counter = () => { let n = 0; return () => ++n; }; |
| Debounce | let t; return (...a) => { clearTimeout(t); t = setTimeout(() => fn(...a), ms); } |
| Throttle | let last = 0; return (...a) => { if (Date.now() - last >= ms) { last = Date.now(); fn(...a); } } |
| Memoize | const cache = new Map(); return x => cache.has(x) ? cache.get(x) : (cache.set(x, fn(x)), cache.get(x)) |
| Once | let done, r; return (...a) => done ? r : ((done = true), (r = fn(...a))) |
| Pipe | const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x) |
| Sleep | const sleep = ms => new Promise(r => setTimeout(r, ms)); |
| Async / await | async function load() { const r = await fetch(url); return r.json(); } |
| Parallel | const [a, b] = await Promise.all([fa(), fb()]); |
| All settled | const results = await Promise.allSettled(promises); |
| First to finish | Promise.race([p, timeout]) |
| In sequence | for (const t of tasks) await t(); (not forEach with async) |
| Concurrency limit | workers = Array.from({ length: limit }, async () => { while (i < tasks.length) await tasks[i++](); }) |
| Retry | for (let n = 0; ; n++) { try { return await fn(); } catch (e) { if (n >= max) throw e; await sleep(delay); } } |
| Timeout wrapper | Promise.race([p, new Promise((_, rej) => setTimeout(() => rej(new Error('timeout')), ms))]) |
| try / catch / finally | try { ... } catch (e) { ... } finally { ... } |
| Custom error | class NotFound extends Error { constructor(m) { super(m); this.name = 'NotFound'; } } |
Gotchas
| == vs === | Always ===. '1' == 1 is true, '1' === 1 is false. |
| Falsy values | false, 0, -0, 0n, '', null, undefined, NaN |
| sort mutates | arr.sort() changes arr; use toSorted or copy first |
| const and objects | const prevents reassignment, not mutation |
| this in arrows | Arrow functions take this from the enclosing scope |
| forEach and await | forEach ignores returned promises; use for...of |
| Floating point | 0.1 + 0.2 !== 0.3; compare with a tolerance or work in cents |
| typeof null | 'object'. Use x === null. |
| JSON and dates | JSON.stringify turns dates into strings; parse them back |
| Array copy | [...arr] and arr.slice() are shallow |
scriptdrill.com: practice every one of these with instant checking. 96 entries.