Home

Published

-

Simple Commands

img of Simple Commands

The working set — things I look up often enough to stop looking up. Grouped so I can find them again.

Regression evaluation metrics

Three metrics for regression problems. All of them are loss functions, so the goal is to minimise them.

   MAE  = 1/n · Σ |yᵢ - ŷᵢ|
MSE  = 1/n · Σ (yᵢ - ŷᵢ)²
RMSE = √( 1/n · Σ (yᵢ - ŷᵢ)² )
  • Mean Absolute Error — the easiest to understand, because it is the average error.
  • Mean Squared Error — more popular than MAE, because it punishes larger errors, which tends to be useful in the real world.
  • Root Mean Squared Error — more popular still, because it is interpretable in the units of y.

JavaScript — arrays

Every line below assumes nums is back to its starting value.

   const nums = [3, 4, 5, 6]

nums.at(1) // 4 — index from the start, or from the end with a negative index
nums.at(-1) // 6 — the last item, without the length-1 dance
nums.pop() // 6 — removes and returns the last item; nums becomes [3, 4, 5]
nums.push(7) // 5 — appends and returns the new length; nums becomes [3, 4, 5, 6, 7]
nums.shift() // 3 — removes and returns the first item; nums becomes [4, 5, 6]
nums.unshift(1) // 5 — prepends and returns the new length; nums becomes [1, 3, 4, 5, 6]
nums.fill(1) // [1, 1, 1, 1] — overwrites every slot in place
nums.join('-') // '3-4-5-6' — stringify with a separator
nums.reverse() // [6, 5, 4, 3] — reverses in place
nums.toReversed() // [6, 5, 4, 3] — same, but nums keeps its order
nums.slice(0, 2) // [3, 4] — copy from start up to (not including) end
nums.includes(5) // true — is the value in there
nums.indexOf(5) // 2 — where it is, or -1 if it isn't
nums.map((num) => num + 6) // [9, 10, 11, 12] — one output per input
nums.find((num) => num > 4) // 5 — the first match itself
nums.findIndex((num) => num > 4) // 2 — the first match's position
nums.filter((num) => num > 4) // [5, 6] — every match
nums.every((num) => num > 5) // false — do all of them pass
nums.some((num) => num % 2 === 0) // true — does at least one pass
nums.reduce((acc, num) => acc + num, 0) // 18 — fold the array into one value

Array.from([1, 2, 3], (x) => x + x) // [2, 4, 6] — build an array and map it in one pass
;[1, 2, [3], [4, 5], 6, []].flatMap((num) => num) // [1, 2, 3, 4, 5, 6] — map, then flatten one level

Sum a property across an array of objects

Two ways; reduce is the one to reach for.

   let sum = 0
incomes.forEach((income) => {
	sum += income.total
})

const sum2 = incomes.reduce((accumulator, income) => {
	return accumulator + income.total
}, 0)

Count occurrences into an object

Reduce into an object when you want a tally keyed by value.

   const pets = ['dog', 'chicken', 'cat', 'dog', 'chicken', 'chicken', 'rabbit']

const petCounts = pets.reduce(function (obj, pet) {
	if (!obj[pet]) {
		obj[pet] = 1
	} else {
		obj[pet]++
	}
	return obj
}, {})

/*
Output:
 {
    dog: 2,
    chicken: 3,
    cat: 1,
    rabbit: 1
 }
 */

Reshape each object in an array

map with a fresh container per item, when the output shape differs from the input.

   const myUsers = [
	{ name: 'shark', likes: 'ocean' },
	{ name: 'turtle', likes: 'pond' },
	{ name: 'otter', likes: 'fish biscuits' }
]

const usersByLikes = myUsers.map((item) => {
	const container = {}

	container[item.name] = item.likes
	container.age = item.name.length * 10

	return container
})

// Output
// [
//     {shark: "ocean", age: 50},
//     {turtle: "pond", age: 60},
//     {otter: "fish biscuits", age: 50}
// ]

Group an array of objects

Object.groupBy buckets by whatever the callback returns — a property, or a computed label.

   const inventory = [
	{ name: 'asparagus', type: 'vegetables', quantity: 5 },
	{ name: 'bananas', type: 'fruit', quantity: 0 },
	{ name: 'goat', type: 'meat', quantity: 23 },
	{ name: 'cherries', type: 'fruit', quantity: 5 },
	{ name: 'fish', type: 'meat', quantity: 22 }
]

const result = Object.groupBy(inventory, ({ type }) => type)

/* Result is:
{
  vegetables: [
    { name: 'asparagus', type: 'vegetables', quantity: 5 },
  ],
  fruit: [
    { name: "bananas", type: "fruit", quantity: 0 },
    { name: "cherries", type: "fruit", quantity: 5 }
  ],
  meat: [
    { name: "goat", type: "meat", quantity: 23 },
    { name: "fish", type: "meat", quantity: 22 }
  ]
}
*/

// Groups can be inferred from values rather than read off a property
function myCallback({ quantity }) {
	return quantity > 5 ? 'ok' : 'restock'
}

const result2 = Object.groupBy(inventory, myCallback)

/* Result is:
{
  restock: [
    { name: "asparagus", type: "vegetables", quantity: 5 },
    { name: "bananas", type: "fruit", quantity: 0 },
    { name: "cherries", type: "fruit", quantity: 5 }
  ],
  ok: [
    { name: "goat", type: "meat", quantity: 23 },
    { name: "fish", type: "meat", quantity: 22 }
  ]
}
*/

Last item in an array

   const lastUser = users[users.length - 1]
// or
const lastUser2 = users.at(-1)

Call an array method on something that isn’t an array

Borrow the method with call when the object is only array-like.

   const arrayLike = {
	length: 3,
	0: 2,
	1: 7.3,
	2: 4
}
console.log(Array.prototype.find.call(arrayLike, (x) => !Number.isInteger(x))) // 7.3

Sum N arrays of different lengths

Pads the short ones with zeros rather than dropping them.

   function sumArrays(...arrays) {
	const n = arrays.reduce((max, xs) => Math.max(max, xs.length), 0)
	const result = Array.from({ length: n })
	return result.map((_, i) => arrays.map((xs) => xs[i] || 0).reduce((sum, x) => sum + x, 0))
}
// sumArrays([0, 1, 2], [1, 2, 3, 4], [1, 2])

JavaScript — Set and Map

Set: unique values, and fast membership checks

   const numbers = [1, 2, 3, 4, 5, 5]

const uniqueNumbers = new Set(numbers) // {1, 2, 3, 4, 5} — dedupes on construction

const mySet1 = new Set()

mySet1.add(1) // Set(1) { 1 }
mySet1.add(5) // Set(2) { 1, 5 }
mySet1.add(5) // Set(2) { 1, 5 } — adding a duplicate is a no-op
mySet1.add('some text') // Set(3) { 1, 5, 'some text' }

const o = { a: 1, b: 2 }
mySet1.add(o)
mySet1.add({ a: 1, b: 2 }) // a different object reference, so this is a second entry

mySet1.has(1) // true
mySet1.has(3) // false, since 3 has not been added to the set
mySet1.has(Math.sqrt(25)) // true — membership is by value for primitives
mySet1.has('Some Text'.toLowerCase()) // true
mySet1.has(o) // true — and by reference for objects

uniqueNumbers.delete(1) // true if it was there

Map: key-value pairs that remember insertion order

Like an object, but any value can be a key and the size is a property away.

   const map1 = new Map()

map1.set('a', 1) // set by key
map1.set('b', 2)
map1.set('c', 3)
map1.get('a') // 1 — read by key
map1.size // 3 — the length
map1.delete('b') // true — it was there
map1.delete('Raymond') // false — it wasn't

map1.set('Jessie', { phone: '213-555-1234', address: '123 N 1st Ave' })
map1.has('Jessie') // true — check before you read
map1.get('Hilary') // undefined

Iterate a Map

entries() returns an iterator of [key, value] pairs in insertion order, so it drops straight into a for...of.

   const map2 = new Map()

map2.set('0', 'foo')
map2.set(1, 'bar')

const iterator1 = map2.entries()

console.log(iterator1.next().value) // ["0", "foo"] — each next() call moves on by one
console.log(iterator1.next().value) // [1, "bar"]

// Frequency map: the first key that only appeared once
for (const [key, value] of frequencies.entries()) {
	if (value == 1) return key
}

JavaScript — loops and short-hands

Long-hand and short-hand side by side, so the short one is obviously the same thing.

   // Iterate with the index
array.forEach(function (arrayItem, index) {
	console.log(arrayItem, index)
})

// Check a value against a list instead of chaining ||
const isVowel = (letter) => ['a', 'e', 'i', 'o', 'u'].includes(letter)

// For of (arrays)
const arr = [1, 2, 3, 4, 5]

// Long-hand
for (let i = 0; i < arr.length; i++) {
	const element = arr[i]
	// ...
}

// Short-hand
for (const element of arr) {
	// ...
}

// For in (objects)
const obj = {
	a: 1,
	b: 2,
	c: 3
}

// Long-hand
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
	const key = keys[i]
	const value = obj[key]
	// ...
}

// Short-hand
for (const key in obj) {
	const value = obj[key]
	// ...
}

// Picking between two calls with the same signature
function f1() {
	// ...
}
function f2() {
	// ...
}

// Long-hand
if (condition) {
	f1()
} else {
	f2()
}

// Short-hand
;(condition ? f1 : f2)()

JavaScript — async

Two independent awaits run one after the other. Promise.all runs them at the same time.

   // Sequential — the second request doesn't start until the first finishes
const user = await getUser()
const products = await getProducts()

// Concurrent — both in flight, resolves when the slower one lands
const [user, products] = await Promise.all([getUser(), getProducts()])

Reading a URL in the browser

location breaks the current URL into its parts.

   location.protocol // 'https:'
location.hostname // 'example.org'
location.port // '8080', or '' on the default port
location.pathname // '/posts/commands'
location.search // '?q=term' — the query string
location.hash // '#section' — the fragment identifier
location.href // the complete URL

TypeScript — Pick and Omit

Derive a type from another instead of writing it twice. Pick names what to keep, Omit names what to drop.

   type User = {
	id: string
	name: string
	age: number
}

type NewUser = Pick<User, 'name' | 'age'>
type NewUser = Omit<User, 'id'>

const newUser: NewUser = {
	name: 'Shanice',
	age: 28
}

TypeScript — debounce

Waits for the calls to stop before firing once. Typed so the wrapper keeps the original signature.

   function debounce<T extends (...args: ChangeEvent<HTMLInputElement>[]) => void>(
	func: T,
	timeout = 300
): (...args: Parameters<T>) => void {
	let timer: NodeJS.Timeout
	return (...args: Parameters<T>) => {
		clearTimeout(timer)
		timer = setTimeout(() => {
			func(...args)
		}, timeout)
	}
}

React — replace conditional rendering with a lookup

A map from role to component, instead of a chain of ternaries. ?? covers the unknown role.

   const RoleViews = {
  GUEST: GuestView,
  ADMIN: AdminView,
  CONTRIBUTOR: ContributorVIew
};

const UserViews = ({role}) => {
  const CurrentViews = RoleViews[role] ?? DefaultView;
  return (
    <div>
      <CurrentView/>
    </div>
  )
}

React — scroll to top on route change

Render it inside the router, above <Routes>, and every navigation starts at the top.

   import { useEffect } from 'react'
import { useLocation } from 'react-router-dom'

export default function ScrollToTop() {
	const { pathname } = useLocation()

	useEffect(() => {
		window.scrollTo(0, 0)
	}, [pathname])

	return null
}

// <ScrollToTop />
// <Routes>

Redux — DevTools with TypeScript

The compose extension isn’t on the Window type, so either cast it or declare it.

   // Cast at the point of use
const composeEnhancers =
	(window['__REDUX_DEVTOOLS_EXTENSION_COMPOSE__'] as typeof compose) || compose

// Or declare it once and use it everywhere
declare global {
	interface Window {
		__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof compose
	}
}

export default function configureStore(initialState: any) {
	const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
	return createStore(
		rootReducer,
		initialState,
		composeEnhancers(applyMiddleware(authMiddleware, logger, thunk))
	)
}

CSS — three ways to centre a div

   <article>
	<div class="classic">Worrap</div>
</article>
   /* Classic — absolute position, then pull back by half its own size */
.classic {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
}

/* Flex — centre on both axes */
.classic {
	display: flex;
	align-items: center;
	justify-content: center;
}

/* Grid — the short one */
.classic {
	display: grid;
	place-items: center;
}

Git — refusing to merge unrelated histories

Two repos with no common commit. Force the merge through once, when you know the histories really are separate.

   git merge --allow-unrelated-histories