Skip to content

Instantly share code, notes, and snippets.

@nwellis
Created June 20, 2025 12:41
Show Gist options
  • Save nwellis/d19b9b2e7d539cc7d3c1bfa7d1355405 to your computer and use it in GitHub Desktop.
Save nwellis/d19b9b2e7d539cc7d3c1bfa7d1355405 to your computer and use it in GitHub Desktop.
A collection of number utils
export function nanToUndefined(val: number | string | undefined) {
if (val === undefined) return val as undefined
switch (typeof val) {
case "number": {
return isNaN(val) ? undefined : val
}
case "string": {
const numVal = Number(val)
return isNaN(numVal) ? undefined : numVal
}
default:
return undefined
}
}
export function clamp(val: number, min: number, max: number) {
return Math.min(max, Math.max(min, val))
}
export function numberRange(startOrEnd: number, end?: number): number[] {
const s = typeof end === "number" ? startOrEnd : 0
const e = typeof end === "number" ? end : startOrEnd
const range = new Array(Math.max(0, e - s));
for (let i = 0; i < range.length; i++) {
range[i] = i + s
}
return range;
}
export function divideWithRemainder(value: number, divisor: number) {
const divisorInteger = Math.floor(divisor)
if (divisorInteger <= 1) return [value]
const quotient = Math.floor(value / divisorInteger)
let remainder = value % divisorInteger
return numberRange(0, divisorInteger).map(() => {
if (remainder > 0) {
remainder -= 1
return quotient + 1
}
return quotient
})
}
export function divideAsEvenAsPossible(total: number, maxGroupSize: number) {
if (!total) return []
if (maxGroupSize < 1) return [total]
const groupCount = Math.ceil(total / maxGroupSize) // 7 / 2 = 4
const remainder = total % groupCount
return numberRange(groupCount)
.map((groupNumber) => Math.floor(total / groupCount) + (groupNumber < remainder ? 1 : 0))
}
export function splitUnevenlyWithRemainder(value: number, weights: number[]) {
if (weights.length < 2) return [value]
const weightTotal = weights.reduce((total, weight) => total + weight, 0)
if (weightTotal <= 0) return weights
let totaled = 0
const weighted = weights
.map((weight, index) => {
const ratio = weight / weightTotal
const evaluated = value * ratio
const noRemainder = Math.floor(evaluated)
totaled += noRemainder
// console.debug(weight, weightTotal, ratio, evaluated, noRemainder)
return [
noRemainder,
evaluated - noRemainder,
index, // retain original order
] as [number, number, number]
})
let remainderLeft = value - totaled
return weighted
// Largest to smallest fractional remainder
.sort((a, b) => b[1] - a[1])
.map(([noRemainder, ...rest]) => {
let adjust = 0
if (remainderLeft > 0) {
remainderLeft -= 1
adjust = 1
}
return [noRemainder + adjust, ...rest] as [number, number, number]
})
.sort(([, , a], [, , b]) => a - b)
.map(([noRemainder]) => noRemainder)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment