froebel
Version:
TypeScript utility library
121 lines (99 loc) • 3.54 kB
JavaScript
var _at;
class SortedArrayImpl {
#data = [];
#cmp;
constructor(compare, ...values) {
this.#cmp = compare;
if (values.length) this.#data = values.sort(compare);
return wrap(this);
}
static from(compare, source) {
return new SortedArrayImpl(compare, ...source);
}
add(...values) {
values.map(this.#addValue);
}
#addValue = (value => {
for (let i = 0; i < this.#data.length; i++) {
if (this.#cmp(value, this.#data[i]) >= 0) continue;
this.#data.splice(i, 0, value);
return;
}
this.#data.push(value);
}).bind(this);
delete = (...indices) => indices.map(i => i >= 0 ? i : this.#data.length + i).map((i, j, a) => {
for (let k = j + 1; k < a.length; k++) if (a[k] > i) a[k]--;
return this.#data.splice(i, 1)[0];
});
clear() {
this.#data = [];
}
#ref(method, i) {
return (f, ...rest) => this.#data[method]((...args) => f(...args.slice(0, i), this, ...args.slice(i + 1)), ...rest);
}
#bind = method => (...args) => this.#data[method](...args);
#wrap = f => (...args) => new SortedArrayImpl(this.#cmp, ...f(...args));
every = this.#ref("every", 2);
filter = this.#wrap(this.#ref("filter", 2));
find = this.#ref("find", 2);
findIndex = this.#ref("findIndex", 2);
forEach = this.#ref("forEach", 2);
includes = this.#bind("includes");
indexOf = this.#bind("indexOf");
join = this.#bind("join");
lastIndexOf = this.#bind("lastIndexOf");
map = this.#ref("map", 2);
pop = this.#bind("pop");
reduce = this.#ref("reduce", 3);
reduceRight = this.#ref("reduceRight", 3);
shift = this.#bind("shift");
slice = this.#wrap(this.#bind("slice"));
some = this.#ref("some", 2);
at = ((_at = this.#data.at) === null || _at === void 0 ? void 0 : _at.bind(this.#data)) ?? (i => this.#data[i >= 0 ? i : this.#data.length + i]);
get length() {
return this.#data.length;
}
[Symbol.iterator]() {
return this.#data[Symbol.iterator]();
}
}
const wrap = v => new Proxy(v, {
get(t, k) {
if (k in t || typeof k !== "string") {
return typeof t[k] === "function" ? t[k].bind(t) : t[k];
}
for (const c of k) if (c < "0" || c > "9") return;
return t.at(parseInt(k));
},
deleteProperty(t, k) {
if (k in t || typeof k !== "string") return delete t[k];
for (const c of k) if (c < "0" || c > "9") return true;
t.delete(parseInt(k));
return true;
}
});
/**
* Sorted array. Behaves much like a regular array but its elements remain
* sorted using the `compare` function supplied in the constructor.
*
* Contains most of the methods defined on regular JavaScript arrays as long as
* they don't modify the array's content in place.
*
* New elements are added using the `add(...values)` method.
*
* Elements can still be accessed using bracket notation as in plain JavaScript
* arrays but can't be assigned to using bracket notation (as that could change
* the element's sort position).
*
* Elements can be removed using the `delete(...indices)` method, which returns
* an array containing the deleted values.
* Deleting an element using `delete sorted[index]` will also work, but results
* in a TypeScript error because element access is marked readonly.
*
* Array methods that pass a reference of the array to a callback (e.g. `map`,
* `reduce`, `find`) will pass a reference to the SortedArray instance instead.
*
* The `filter` and `slice` methods will return SortedArray instances instead of
* plain arrays.
*/
export default SortedArrayImpl;