@thi.ng/sorted-map
Version:
Skiplist-based sorted map & set implementation
353 lines (352 loc) • 9.87 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __typeError = (msg) => {
throw TypeError(msg);
};
var __decorateClass = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
if (kind && result) __defProp(target, key, result);
return result;
};
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
var __privateWrapper = (obj, member, setter, getter) => ({
set _(value) {
__privateSet(obj, member, value, setter);
},
get _() {
return __privateGet(obj, member, getter);
}
});
var _head, _cmp, _p, _rnd, _size;
import { dissoc } from "@thi.ng/associative/dissoc";
import { __disposableEntries } from "@thi.ng/associative/internal/dispose";
import { __equivMap } from "@thi.ng/associative/internal/equiv";
import { __tostringMixin } from "@thi.ng/associative/internal/tostring";
import { into } from "@thi.ng/associative/into";
import { isPlainObject } from "@thi.ng/checks/is-plain-object";
import { compare } from "@thi.ng/compare/compare";
import { SYSTEM } from "@thi.ng/random/system";
import { isReduced } from "@thi.ng/transducers/reduced";
class Node {
constructor(k, v, level = 0) {
this.k = k;
this.v = v;
this.level = level;
}
next;
prev;
up;
down;
}
let SortedMap = class extends Map {
constructor(pairs, opts = {}) {
super();
__privateAdd(this, _head);
__privateAdd(this, _cmp);
__privateAdd(this, _p);
__privateAdd(this, _rnd);
__privateAdd(this, _size);
__privateSet(this, _head, new Node());
__privateSet(this, _cmp, opts.compare || compare);
__privateSet(this, _rnd, opts.rnd || SYSTEM);
__privateSet(this, _p, opts.probability || 1 / Math.E);
__privateSet(this, _size, 0);
if (pairs) {
this.into(pairs);
}
}
get size() {
return __privateGet(this, _size);
}
get [Symbol.species]() {
return SortedMap;
}
get [Symbol.toStringTag]() {
return "SortedMap";
}
*[Symbol.iterator]() {
let node = this.firstNode();
while (node?.k !== void 0) {
yield [node.k, node.v];
node = node.next;
}
}
// mixin
[Symbol.dispose]() {
}
/**
* Yields iterator of sorted `[key, value]` pairs, optionally taking given
* `key` and `max` flag into account.
*
* @remarks
* If `key` is given and `max=false`, the key is used as minimum search key
* and the iterator will only yield pairs for which keys are `>=` given key.
* If `max=true`, the given is used as maximum and only yields pairs for
* which keys are `<=` given key.
*
* If **no** key is given, yields **all** pairs.
*
* @param key
* @param max
*/
*entries(key, max = false) {
if (key === void 0) {
yield* this;
return;
}
if (!__privateGet(this, _size)) return;
const cmp = __privateGet(this, _cmp);
if (max) {
let node = this.firstNode();
while (node?.k !== void 0 && cmp(node.k, key) <= 0) {
yield [node.k, node.v];
node = node.next;
}
} else {
let node = this.firstNode();
while (node.down) node = node.down;
while (node) {
if (node.k !== void 0 && cmp(node.k, key) >= 0) {
yield [node.k, node.v];
}
node = node.next;
}
}
}
/**
* Similar to {@link SortedMap.entries}, but only yield sequence of keys.
*
* @param key
* @param max
*/
*keys(key, max = false) {
for (let p of this.entries(key, max)) {
yield p[0];
}
}
/**
* Similar to {@link SortedMap.entries}, but only yield sequence of values.
*
* @param key
* @param max
*/
*values(key, max = false) {
for (let p of this.entries(key, max)) {
yield p[1];
}
}
clear() {
__privateSet(this, _head, new Node());
__privateSet(this, _size, 0);
}
empty() {
return new SortedMap(null, this.opts());
}
copy() {
return new SortedMap(this, this.opts());
}
compare(o) {
const n = this.size;
const m = o.size;
if (n < m) return -1;
if (n > m) return 1;
const i = this.entries();
const j = o.entries();
let x, y;
let c;
while (x = i.next(), y = j.next(), !x.done && !y.done) {
if ((c = compare(x.value[0], y.value[0])) !== 0 || (c = compare(x.value[1], y.value[1])) !== 0) {
return c;
}
}
return 0;
}
equiv(o) {
return __equivMap(this, o);
}
first() {
const node = this.firstNode();
return node?.k !== void 0 ? [node.k, node.v] : void 0;
}
get(key, notFound) {
const node = this.findNode(key);
return node.k !== void 0 && __privateGet(this, _cmp).call(this, node.k, key) === 0 ? node.v : notFound;
}
has(key) {
const node = this.findNode(key);
return node.k !== void 0 && __privateGet(this, _cmp).call(this, node.k, key) === 0;
}
set(key, val) {
let node = this.findNode(key);
if (node.k !== void 0 && __privateGet(this, _cmp).call(this, node.k, key) === 0) {
node.v = val;
while (node.down) {
node = node.down;
node.v = val;
}
return this;
}
let newNode = new Node(key, val, node.level);
this.insertInLane(node, newNode);
let currLevel = node.level;
let headLevel = __privateGet(this, _head).level;
const rnd = __privateGet(this, _rnd);
const p = __privateGet(this, _p);
while (rnd.probability(p)) {
if (currLevel >= headLevel) {
const newHead = new Node(
void 0,
void 0,
headLevel + 1
);
this.linkLanes(newHead, __privateGet(this, _head));
__privateSet(this, _head, newHead);
headLevel++;
}
while (!node.up) node = node.prev;
node = node.up;
const tmp = new Node(key, val, node.level);
this.insertInLane(node, tmp);
this.linkLanes(tmp, newNode);
newNode = tmp;
currLevel++;
}
__privateWrapper(this, _size)._++;
return this;
}
delete(key) {
let node = this.findNode(key);
if (node.k === void 0 || __privateGet(this, _cmp).call(this, node.k, key) !== 0) return false;
while (node.down) node = node.down;
let prev;
let next;
while (node) {
prev = node.prev;
next = node.next;
if (prev) prev.next = next;
if (next) next.prev = prev;
node = node.up;
}
while (!__privateGet(this, _head).next && __privateGet(this, _head).down) {
__privateSet(this, _head, __privateGet(this, _head).down);
__privateGet(this, _head).up = void 0;
}
__privateWrapper(this, _size)._--;
return true;
}
into(pairs) {
return into(this, pairs);
}
dissoc(keys) {
return dissoc(this, keys);
}
/**
* The key & value args given the callback `fn` MUST be treated as
* readonly/immutable. This could be enforced via TS, but would
* break ES6 Map interface contract.
*
* @param fn -
* @param thisArg -
*/
forEach(fn, thisArg) {
for (let p of this) {
fn.call(thisArg, p[1], p[0], this);
}
}
$reduce(rfn, acc) {
let node = this.firstNode();
while (node?.k !== void 0 && !isReduced(acc)) {
acc = rfn(acc, [node.k, node.v]);
node = node.next;
}
return acc;
}
opts() {
return {
compare: __privateGet(this, _cmp),
probability: __privateGet(this, _p),
rnd: __privateGet(this, _rnd)
};
}
/**
* Inserts `b` as successor of `a` (in the same lane as `a`).
*
* @param a
* @param b
*/
insertInLane(a, b) {
b.prev = a;
b.next = a.next;
if (a.next) a.next.prev = b;
a.next = b;
}
/**
* Links lanes by connecting `a` and `b` vertically.
*
* @param a
* @param b
*/
linkLanes(a, b) {
a.down = b;
b.up = a;
}
/**
* Returns first node on lowest level. Unless the map is empty, this node
* will be the first data node (with the smallest key).
*/
firstNode() {
let node = __privateGet(this, _head);
while (node.down) node = node.down;
while (node.prev) node = node.prev;
if (node.next) node = node.next;
return node;
}
/**
* Returns the first matching (or predecessor) node for given key (NOT
* necessarily at the lowest level).
*
* @param key
*/
findNode(key) {
const cmp = __privateGet(this, _cmp);
let node = __privateGet(this, _head);
let next;
let down;
let nodeKey;
while (true) {
next = node.next;
while (next && cmp(next.k, key) <= 0) {
node = next;
next = node.next;
}
nodeKey = node.k;
if (nodeKey !== void 0 && cmp(nodeKey, key) === 0) break;
down = node.down;
if (!down) break;
node = down;
}
return node;
}
};
_head = new WeakMap();
_cmp = new WeakMap();
_p = new WeakMap();
_rnd = new WeakMap();
_size = new WeakMap();
SortedMap = __decorateClass([
__disposableEntries,
__tostringMixin
], SortedMap);
function defSortedMap(src, opts) {
return isPlainObject(src) ? new SortedMap(Object.entries(src), opts) : new SortedMap(src, opts);
}
export {
SortedMap,
defSortedMap
};