import {throttle} from 'throttle-debounce' import {bech32, utf8} from "@scure/base" export type Nil = null | undefined export const now = () => Math.round(Date.now() / 1000) export const nth = (i: number) => (xs: T[]) => xs[i] export const first = (xs: T[], ...args: unknown[]) => xs[0] export const last = (xs: T[], ...args: unknown[]) => xs[xs.length - 1] export const prop = (k: string) => (x: Record) => x[k] export const identity = (x: T) => x export const inc = (x: number | Nil) => (x || 0) + 1 export const dec = (x: number | Nil) => (x || 0) - 1 export const max = (xs: number[]) => xs.reduce((a, b) => Math.max(a, b), 0) export const min = (xs: number[]) => xs.reduce((a, b) => Math.min(a, b), 0) export const sum = (xs: number[]) => xs.reduce((a, b) => a + b, 0) export const avg = (xs: number[]) => sum(xs) / xs.length export const between = (low: number, high: number, n: number) => n > low && n < high export const flatten = (xs: T[]) => xs.flatMap(identity) export const uniq = (xs: T[]) => Array.from(new Set(xs)) export const choice = (xs: T[]): T => xs[Math.floor(xs.length * Math.random())] export const shuffle = (xs: Iterable): T[] => Array.from(xs).sort(() => Math.random() > 0.5 ? 1 : -1) export const randomId = (): string => Math.random().toString().slice(2) export const isIterable = (x: any) => Symbol.iterator in Object(x) export const toIterable = (x: any) => isIterable(x) ? x : [x] export const stripProtocol = (url: string) => url.replace(/.*:\/\//, "") export const ensurePlural = (x: T | T[]) => (x instanceof Array ? x : [x]) export const hash = (s: string) => Math.abs(s.split("").reduce((a, b) => ((a << 5) - a + b.charCodeAt(0)) | 0, 0)).toString() export const sortBy = (f: (x: T) => number, xs: T[]) => xs.sort((a: T, b: T) => f(a) - f(b)) export const groupBy = (f: (x: T) => string, xs: T[]) => { const r: Record = {} for (const x of xs) { const k = f(x) if (!r[k]) { r[k] = [] } r[k].push(x) } return r } export const sample = (n: number, xs: T[]) => { const result: T[] = [] const limit = Math.min(n, xs.length) for (let i = 0; i < limit; i++) { result.push(xs.splice(Math.floor(xs.length * Math.random()), 1)[0]) } return result } export const initArray = (n: number, f: () => T) => { const result = [] for (let i = 0; i < n; i++) { result.push(f()) } return result } export const chunk = (chunkLength: number, coll: T[]) => { const result: T[][] = [] const current: T[] = [] for (const item of coll) { if (current.length < chunkLength) { current.push(item) } else { result.push(current.splice(0)) } } if (current.length > 0) { result.push(current) } return result } export const chunks = (n: number, coll: T[]) => { const result: T[][] = initArray(n, () => []) for (let i = 0; i < coll.length; i++) { result[i % n].push(coll[i]) } return result } export const batch = (t: number, f: (xs: T[]) => void) => { const xs: T[] = [] const cb = throttle(t, () => xs.length > 0 && f(xs.splice(0))) return (x: T) => { xs.push(x) cb() } } export const addToMapKey = (m: Map>, k: string, v: T) => { const a = m.get(k) || new Set() a.add(v) m.set(k, a) } export const pushToMapKey = (m: Map, k: string, v: T) => { const a = m.get(k) || [] a.push(v) m.set(k, a) } export const hexToBech32 = (prefix: string, url: string) => bech32.encode(prefix, bech32.toWords(utf8.decode(url)), false) export const bech32ToHex = (b32: string) => utf8.encode(bech32.fromWords(bech32.decode(b32, false).words)) // https://github.com/microsoft/TypeScript/issues/4628#issuecomment-1147905253 export type OmitStatics = T extends {new(...args: infer A): infer R} ? {new(...args: A): R}&Omit : Omit; // https://github.com/microsoft/TypeScript/issues/4628#issuecomment-1147905253 export type OmitAllStatics = T extends {new(...args: infer A): infer R, prototype: infer P} ? {new(...args: A): R, prototype: P} : never;