UNPKG

@thi.ng/sorted-map

Version:

Skiplist-based sorted map & set implementation

353 lines (352 loc) 9.87 kB
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 };