Skip to content

Instantly share code, notes, and snippets.

@dfoverdx
Last active February 13, 2022 20:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dfoverdx/cbbcb850bbaa55d42952f58629cb99cf to your computer and use it in GitHub Desktop.
Save dfoverdx/cbbcb850bbaa55d42952f58629cb99cf to your computer and use it in GitHub Desktop.
Scriptable extensions.js
importModule('./lib/extensions')({
// only need to pass in the types you need extended
// for this script
Math,
Number,
Array,
})
console.log(
Math.getFactors((6).factorial())
.shuffle()
.joinNL()
)
const [
_Object = Object,
_Number = Number,
_Array = Array,
_BigInt = BigInt,
_Math = Math,
_String = String,
_Promise = Promise,
] = []
// used to ensure each type only gets extended once
// per sandbox
const extended = new Set()
const ex = (type, fn) => {
if (!extended.has(type)) {
fn()
extended.add(type)
}
}
const _primes = [2, 3]
const _primesSet = new Set(_primes)
const _memoedFactorials = [1, 1]
const _max = Math.max;
const _min = Math.min;
const extendNatives = module.exports = ({
Object = _Object,
Number = _Number,
Array = _Array,
BigInt = _BigInt,
Math = _Math,
String = _String,
Promise = _Promise,
}) => {
ex(Array, () => {
Object.assign(Array.prototype, {
eachAnd(fn) {
this.forEach(fn)
return this
},
unique() {
return Array.from(new Set(this))
},
shuffle(inPlace = true) {
const arr = inPlace ? this : this.slice()
return arr.eachAnd((_, idx) => {
const sIdx =
Math.randInt(idx, arr.length - 1)
[arr[idx], arr[sIdx]] =
[arr[sIdx], arr[idx]]
})
},
chooseRandom() {
return this[Math.randInt(this.length)]
},
joinNL() {
return this.join('\n')
},
})
Object.defineProperty(Array.prototype, 'last', {
get() {
if (!this.length) {
return undefined
}
return this[this.length - 1]
},
set(value) {
if (!this.length) {
throw new Error(
'Cannot assign last to empty array'
)
}
return (this[this.length - 1] = value)
},
})
})
ex(BigInt, () => {
BigInt.isBigInt = n =>
typeof n === 'bigint' || n instanceof BigInt
BigInt.divide = (n, d) => {
const val = Math.log(n) - Math.log(d)
if (Number.isInteger(val)) {
return n / d
}
return val
};
BigInt.prototype.divideBy = function (d) {
return BigInt.divide(this, d)
}
})
ex(Number, () => {
Number.isBigInt = BigInt.isBigInt
Number.memoedPrimes = _primes
Number.memoedPrimesSet = _primesSet
Number.prototype.divideBy = function (d) {
if (!BigInt.isBigInt(d)) {
return this / d
}
return BigInt.divide(this, d)
};
Number.prototype.factorial =
BigInt.prototype.factorial =
function () { return Math.factorial(this) }
Number.range = function* (min, max, step) {
if (max == null) {
[min, max] = [0, min]
}
step = step != null
? step
: min <= max
? 1
: -1
for (let i = min; i < max; i += step) {
yield i
}
}
Number.rangeArray = (min, max, step) => {
if (step === 0) {
throw new Error('step cannot be 0')
}
if (step != null) {
if (step > 0 && max < min) {
throw new Error(
`step (${step}) must be positive when max (${max}) < min (${min})`
)
} else if (step < 0 && min < max) {
throw new Error(
`step (${step}) must be negative when min (${min}) < max (${max})`
)
}
}
return [...Number.range(min, max, step)]
}
Number.isPrime = n => n.isPrime()
Number.prototype.isPrime = function () {
const n = Number(this)
if (_primesSet.has(n)) {
return true;
}
return [
...Math.genPrimes(_primes.last + 2, n)
].last === n
}
Number.prototype.divides = function (n) {
return !(n % this)
}
Number.compare = (a, b) => a - b
})
ex(Math, () => {
Math.factorial = function factorial(n) {
if (_memoedFactorials[n]) {
return _memoedFactorials[n]
}
const nM1F = factorial(n - 1)
if (
BigInt.isBigInt(n) ||
BigInt.isBigInt(nM1F)
) {
return BigInt(n) * BigInt(nM1F)
}
return (_memoedFactorials[n] = n * nM1F)
}
Math.nCr = function (n, r) {
if (r > n) {
return 0
}
n = BigInt(n)
let c = BigInt(1)
for (let d = BigInt(1); d <= r; d++) {
c *= n--
c /= d
}
return c > Number.MAX_SAFE_INTEGER
? c
: Number(c)
}
Math.max = (...vals) =>
vals.flat().reduce((m, v) =>
Number.isNaN(m)
? v
: _max(m, v),
NaN)
Math.min = (...vals) =>
vals.reduce((m, v) =>
Number.isNaN(m)
? v
: _min(m, v),
NaN)
Math.sum = (...values) =>
values.reduce((a, f) => a + f, 0)
Math.product = (...values) =>
values.reduce((a, f) => a * f, 1)
Math.avg = (...vals) =>
Math.sum(...vals) / vals.length
Math.genPrimes = function* (
start = 2,
end = Infinity
) {
if (start <= _primes.last) {
yield* _primes
.filter(p => p >= start && p <= end)
}
for (
let p = _primes.last + 2;
p <= end;
p += 2
) {
const rootP = Math.ceil(Math.sqrt(p))
let isPrime = true
for (
let i = 0, n = _primes[i];
i < _primes.length && n <= rootP;
i++, n = _primes[i]
) {
if (!(p % n)) {
isPrime = false
break
}
}
if (isPrime) {
_primes.push(p)
_primesSet.add(p)
yield p
}
}
}
/** @param {any[]} arr */
const weave = arr => arr.length < 3
? arr
: [arr[0], ...weave(arr.slice(2)), arr[1]]
Math.getFactors = n => weave(
Number.rangeArray(
1,
Math.floor(Math.sqrt(n)) + 1
)
.filter(d => d.divides(n))
.flatMap(d => (n === d * d ? d : [d, n / d]))
)
Math.getPrimeFactors = n =>
Math.getFactors(n).filter(Number.isPrime)
Math.primeFactorization = n => {
/** @type {number[]} */
const result = [];
const gen = Math.genPrimes()
while (n > 1) {
const d = gen.next().value
while (!(n % d)) {
result.push(d)
n /= d
}
}
return result
}
Math.roundTo = (n, d) =>
Math.round(n * 10 ** d) / d
Math.randInt = (minOrMax, maxOrUndefined) => {
const [min, max] = maxOrUndefined == null
? [0, minOrMax]
: [minOrMax, maxOrUndefined + 1]
const range = max - min
return Math.floor(Math.random() * range) + min
}
})
ex(Promise, () => {
Promise.sleep = (ms = 0) =>
new Promise(res => setTimeout(res, ms))
})
ex(String, () => {
Object.entries(
Object.getOwnPropertyDescriptors(
Array.prototype
)
)
.filter(([n, d]) =>
!(n in String.prototype) &&
typeof d.value === 'function'
)
.forEach(([n]) =>
String.prototype[n] = String.prototype[n] ||
function (...args) {
return Array.from(this)[n](...args)
}
)
String.prototype.splitNL = function () {
return this.split(/\r?\n/)
}
})
ex(Object, () => {
Object.mapObject = (obj, fn) =>
Object.fromEntries(Object.entries(obj).map(fn))
})
}
extendNatives({})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment