export function formatTime(timeMs: number) { const leadingZeros = "00" + Math.floor(timeMs) % 1000; const formatted = `${Math.floor(timeMs / 1000)}.${(leadingZeros).substring(leadingZeros.length-3)}`; return formatted; } export function compactTime(timeMs: number) { if (timeMs % 1000 === 0) { return `${timeMs / 1000}s`; } return `${timeMs} ms`; } export function memoize(fn: (i: InType) => OutType) { const cache = new Map(); return (i: InType) => { const found = cache.get(i); if (found) { return found; } const result = fn(i); cache.set(i, result); return result; } } // compare arrays by value export function arraysEqual(a: T[], b: T[], cmp: (a: T, b: T) => boolean = (a,b)=>a===b): boolean { if (a === b) return true; if (a.length !== b.length) return false; for (let i=0; i(a: Set, b: Set): boolean { if (a === b) return true; if (a.size !== b.size) return false; for (const itemA of a) if (!b.has(itemA)) return false; return true; } export function objectsEqual(a: {[key: string]: T}, b: {[key: string]: T}, cmp: (a: T, b: T) => boolean = (a,b)=>a===b): boolean { if (a === b) return true; if (Object.keys(a).length !== Object.keys(b).length) return false; for (const [keyA, valueA] of Object.entries(a)) if (!cmp(b[keyA], valueA)) return false; return true; } export function mapsEqual(a: Map, b: Map, cmp: (a: V, b: V) => boolean = (a,b)=>a===b) { if (a===b) return true; if (a.size !== b.size) return false; for (const [keyA,valA] of a.entries()) { const valB = b.get(keyA); if (valB === undefined) return false; if (!cmp(valA, valB)) return false; } return true; } export function withGrow(arr: T[], i: number, value: T, fill: T) { if (i >= arr.length) { arr = [...arr, ...new Array(i - arr.length + 1).map(_ => fill)]; } return arr.with(i, value); }