@jrc03c/js-math-tools
Version:
some math tools for JS
210 lines (164 loc) • 3.91 kB
JavaScript
import { assert } from "./assert.mjs"
import { isNumber } from "./is-number.mjs"
import { isUndefined } from "./is-undefined.mjs"
class RangeIterator {
static from(data) {
return new RangeIterator(data.a, data.b)
}
a = 0
b = 0
step = 0
constructor(a, b, step) {
this.a = a
this.b = b
this.step = step ?? 1
}
get length() {
return Math.abs(
(Math.max(this.a, this.b) - Math.min(this.a, this.b)) / this.step,
)
}
get pairIterator() {
const iterator = this[Symbol.iterator]()
function* helper() {
let i = 0
for (const v of iterator) {
yield [v, i]
i++
}
}
return helper()
}
[Symbol.iterator]() {
const shouldIncludeBigInts =
typeof this.a === "bigint" ||
typeof this.b === "bigint" ||
typeof this.step === "bigint"
const a = shouldIncludeBigInts ? BigInt(this.a) : this.a
const b = shouldIncludeBigInts ? BigInt(this.b) : this.b
let step = shouldIncludeBigInts ? BigInt(this.step) : this.step
if ((a <= b && step < 0) || (a > b && step > 0)) {
step *= shouldIncludeBigInts ? BigInt(-1) : -1
}
function* helper() {
if (a <= b) {
for (let i = a; i < b; i += step) {
yield i
}
} else {
for (let i = a; i > b; i += step) {
yield i
}
}
}
return helper()
}
drop(limit) {
return new RangeIterator(this.a + limit * this.step, this.b)
}
every(fn) {
for (const pair of this.pairIterator) {
if (!fn(...pair)) {
return false
}
}
return true
}
filter(fn) {
const out = []
for (const pair of this.pairIterator) {
if (fn(...pair)) {
out.push(pair[0])
}
}
return out
}
find(fn) {
for (const pair of this.pairIterator) {
if (fn(...pair)) {
return pair[0]
}
}
}
flatMap() {
throw new Error("The `RangeIterator.flatMap` method has no implementation!")
}
forEach(fn) {
for (const pair of this.pairIterator) {
fn(...pair)
}
}
map(fn) {
const out = []
for (const pair of this.pairIterator) {
out.push(fn(...pair))
}
return out
}
reduce(fn, out) {
for (const pair of this.pairIterator) {
out = fn(pair[0], out, pair[1])
}
return out
}
some(fn) {
for (const pair of this.pairIterator) {
if (fn(...pair)) {
return true
}
}
return false
}
take(limit) {
return new RangeIterator(this.a, this.a + limit * this.step)
}
toArray() {
const out = []
for (const i of this) {
out.push(i)
}
return out
}
}
function range(a, b, step = 1) {
assert(
!isUndefined(a) && !isUndefined(b) && !isUndefined(step),
"You must pass two numbers and optionally a step value to the `range` function!",
)
assert(
isNumber(a) && isNumber(b) && isNumber(step),
"You must pass two numbers and optionally a step value to the `range` function!",
)
assert(
step !== 0,
"The step value must be greater than 0! (NOTE: The step value is a magnitude; it does not indicate direction.)",
)
return new RangeIterator(a, b, step)
// let shouldReverse = false
// const shouldIncludeBigInts =
// typeof a === "bigint" || typeof b === "bigint" || typeof step === "bigint"
// a = Number(a)
// b = Number(b)
// step = Number(step)
// if (a > b) {
// shouldReverse = true
// const buffer = a
// a = b + step
// b = buffer + step
// }
// let out = []
// for (let i = a; i < b; i += step) {
// if (shouldIncludeBigInts) {
// try {
// out.push(BigInt(i))
// } catch (e) {
// out.push(i)
// }
// } else {
// out.push(i)
// }
// }
// if (shouldReverse) out = reverse(out)
// return out
}
export { range, RangeIterator }