JavaScript utilities to mimic Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** {@link https://gist.github.com/xmodar/d3a17bf51b8399534c5f8d27104a2a38} */ | |
export const operator = { | |
lt: <T>(a: T, b: T) => a < b, | |
le: <T>(a: T, b: T) => a <= b, | |
eq: <T>(a: T, b: T) => a === b, | |
ne: <T>(a: T, b: T) => a !== b, | |
ge: <T>(a: T, b: T) => a >= b, | |
gt: <T>(a: T, b: T) => a > b, | |
not: <T>(a: T) => !a, | |
abs: (a: number) => Math.abs(a), | |
add: (a: number, b: number) => a + b, | |
and_: (a: number, b: number) => a & b, | |
floordiv: (a: number, b: number) => (a / b) >> 0, | |
inv: <T>(a: T) => ~a, | |
lshift: (a: number, b: number) => a << b, | |
mod: (a: number, b: number) => a % b, | |
mul: (a: number, b: number) => a * b, | |
neg: <T>(a: T) => -a, | |
or_: (a: number, b: number) => a | b, | |
pow: (a: number, b: number) => a ** b, | |
rshift: (a: number, b: number) => a >> b, | |
sub: (a: number, b: number) => a - b, | |
truediv: (a: number, b: number) => a / b, | |
xor: (a: number, b: number) => a ^ b, | |
}; | |
export function list<T>(iterable: Iterable<T> | undefined = undefined): T[] { | |
return iterable === undefined ? [] : Array.from(iterable); | |
} | |
export function* iter<T>(iterable: Iterable<T>): Generator<T> { | |
for (const value of iterable) yield value; | |
} | |
export function* range( | |
start: number, | |
stop: number | undefined = undefined, | |
step = 1, | |
): Generator<number> { | |
if (step === 0) throw new Error('step cannot be zero'); | |
if (stop === undefined) [start, stop] = [0, start]; | |
const notDone = step > 0 ? operator.lt : operator.gt; | |
while (notDone(start, stop)) { | |
yield start; | |
start += step; | |
} | |
} | |
export function* enumerate<T>( | |
iterable: Iterable<T>, | |
start = 0, | |
): Generator<[number, T]> { | |
for (const value of iterable) yield [start++, value]; | |
} | |
export function* reversed<T>(iterable: T[]): Generator<T> { | |
for (let i = iterable.length - 1; i >= 0; i--) yield iterable[i]; | |
} | |
export function* map<S, T>( | |
callable: (a: S) => T, | |
iterable: Iterable<S>, | |
): Generator<T> { | |
for (const value of iterable) yield callable(value); | |
} | |
export function* filter<T>( | |
callable: (a: T) => boolean, | |
iterable: Iterable<T>, | |
): Generator<T> { | |
for (const value of iterable) if (callable(value)) yield value; | |
} | |
export function reduce<S, T>( | |
callable: (a: T, b: S) => T, | |
iterable: Iterable<S>, | |
initial: T | undefined = undefined, | |
): T { | |
let first = initial === undefined; | |
for (const value of iterable) { | |
if (first) { | |
first = false; | |
initial = value as unknown as T; | |
} else { | |
initial = callable(initial as T, value); | |
} | |
} | |
if (initial === undefined) throw new Error('provide at least one value'); | |
return initial; | |
} | |
export function sum(iterable: Iterable<number>, start = 0): number { | |
for (const value of iterable) start += value; | |
return start; | |
} | |
export function prod(iterable: Iterable<number>, start = 1): number { | |
for (const value of iterable) start *= value; | |
return start; | |
} | |
export function all<T>(iterable: Iterable<T>): boolean { | |
for (const value of iterable) if (!value) return false; | |
return true; | |
} | |
export function any<T>(iterable: Iterable<T>): boolean { | |
for (const value of iterable) if (value) return true; | |
return false; | |
} | |
type Zip<T extends Iterable<any>[]> = { | |
[I in keyof T]: T[I] extends Iterable<infer E> ? E : never; | |
}; | |
export function* zip<T extends Iterable<any>[]>(...iterables: T) { | |
if (iterables.length === 0) return; | |
const _iterables = list(map(iter, iterables)); | |
while (true) { | |
const pair = []; | |
for (const iterable of _iterables) { | |
const item = iterable.next(); | |
if (item.done) return; | |
pair.push(item.value); | |
} | |
yield pair as Zip<T>; | |
} | |
} | |
export function* product<T extends any[][]>(...iterables: T) { | |
const count = [1]; | |
for (const iterable of reversed(iterables)) { | |
count.unshift(iterable.length * count[0]); | |
} | |
for (const i of range(count.shift() as number)) { | |
const pair = []; | |
for (const [j, iterable] of zip(count, iterables as any[][])) { | |
pair.push(iterable[Math.floor(i / j) % iterable.length]); | |
} | |
yield pair as Zip<T>; | |
} | |
} | |
export function uuid4() { | |
// https://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid | |
const randByte = () => crypto.getRandomValues(new Uint8Array(1))[0]; | |
const shuffle = (c: number) => c ^ (randByte() & (15 >> (c / 4))); | |
const randHex = (c: string) => shuffle(parseInt(c)).toString(16); | |
return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, randHex); | |
} | |
export function uuid4Int() { | |
return BigInt('0x' + uuid4().replace(/-/g, '')); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment