UNPKG

@thi.ng/associative

Version:

ES Map/Set-compatible implementations with customizable equality semantics & supporting operations

231 lines (230 loc) 6.81 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 _hash, _equiv, _load, _bins, _mask, _size; import { ceilPow2 } from "@thi.ng/binary/pow"; import { isPlainObject } from "@thi.ng/checks/is-plain-object"; import { equiv } from "@thi.ng/equiv"; import { map } from "@thi.ng/transducers/map"; import { dissoc } from "./dissoc.js"; import { __disposableEntries } from "./internal/dispose.js"; import { __equivMap } from "./internal/equiv.js"; import { __tostringMixin } from "./internal/tostring.js"; import { into } from "./into.js"; const DEFAULT_CAP = 16; const DEFAULT_LOAD = 0.75; let HashMap = class extends Map { constructor(pairs, opts) { super(); __privateAdd(this, _hash); __privateAdd(this, _equiv); __privateAdd(this, _load); __privateAdd(this, _bins); __privateAdd(this, _mask); __privateAdd(this, _size); const m = ceilPow2(Math.min(opts.cap || DEFAULT_CAP, 4)) - 1; __privateSet(this, _hash, opts.hash); __privateSet(this, _equiv, opts.equiv || equiv); __privateSet(this, _load, opts.load || DEFAULT_LOAD); __privateSet(this, _mask, m); __privateSet(this, _bins, new Array(m + 1)); __privateSet(this, _size, 0); if (pairs) { this.into(pairs); } } get [Symbol.species]() { return HashMap; } get [Symbol.toStringTag]() { return "HashMap"; } get size() { return __privateGet(this, _size); } [Symbol.iterator]() { return this.entries(); } // mixin [Symbol.dispose]() { } *entries() { for (const p of __privateGet(this, _bins)) { if (p) yield [p[0], p[1]]; } } *keys() { for (const p of __privateGet(this, _bins)) { if (p) yield p[0]; } } *values() { for (const p of __privateGet(this, _bins)) { if (p) yield p[1]; } } /** * 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 (const pair of __privateGet(this, _bins)) { fn.call(thisArg, pair[1], pair[0], this); } } clear() { __privateSet(this, _bins, new Array(DEFAULT_CAP)); __privateSet(this, _mask, 15); __privateSet(this, _size, 0); } empty() { return new HashMap(null, this.opts({ cap: DEFAULT_CAP })); } copy() { const m = new HashMap(null, this.opts({ cap: 4 })); __privateSet(m, _bins, __privateGet(this, _bins).slice()); __privateSet(m, _mask, __privateGet(this, _mask)); __privateSet(m, _size, __privateGet(this, _size)); return m; } equiv(o) { return __equivMap(this, o); } has(key) { const i = this.find(key); return i >= 0 && __privateGet(this, _bins)[i] != void 0; } get(key, notFound) { const i = this.find(key); return i >= 0 && __privateGet(this, _bins)[i] ? __privateGet(this, _bins)[i][1] : notFound; } set(key, val) { let i = this.find(key); if (i >= 0 && __privateGet(this, _bins)[i]) { __privateGet(this, _bins)[i][1] = val; return this; } if (this.size > __privateGet(this, _mask) * __privateGet(this, _load)) { this.resize(); i = this.find(key); } __privateGet(this, _bins)[i] = [key, val]; __privateWrapper(this, _size)._++; return this; } delete(key) { const bins = __privateGet(this, _bins); const mask = __privateGet(this, _mask); let i = this.find(key); if (i >= 0 && !bins[i]) { return false; } __privateWrapper(this, _size)._--; let j = i; let k; while (true) { delete bins[i]; do { j = j + 1 & mask; if (!bins[j]) return true; k = __privateGet(this, _hash).call(this, bins[j][0]) & mask; } while (i <= j ? i < k && k <= j : i < k || k <= j); bins[i] = bins[j]; i = j; } } into(pairs) { return into(this, pairs); } dissoc(keys) { return dissoc(this, keys); } opts(overrides) { return { hash: __privateGet(this, _hash), equiv: __privateGet(this, _equiv), load: __privateGet(this, _load), cap: __privateGet(this, _mask) + 1, ...overrides }; } /** @internal */ find(key) { const bins = __privateGet(this, _bins); const mask = __privateGet(this, _mask); const equiv2 = __privateGet(this, _equiv); let h = __privateGet(this, _hash).call(this, key) & __privateGet(this, _mask); let i = mask; while (bins[h] && !equiv2(bins[h][0], key)) { i--; if (i < 0) return -1; h = h + 1 & mask; } return h; } /** @internal */ resize() { const src = __privateGet(this, _bins); const cap = (__privateGet(this, _mask) + 1) * 2; __privateSet(this, _bins, new Array(cap)); __privateSet(this, _mask, cap - 1); __privateSet(this, _size, 0); for (const p of src) { if (p) this.set(p[0], p[1]); } } }; _hash = new WeakMap(); _equiv = new WeakMap(); _load = new WeakMap(); _bins = new WeakMap(); _mask = new WeakMap(); _size = new WeakMap(); HashMap = __decorateClass([ __disposableEntries, __tostringMixin ], HashMap); function defHashMap(src, opts) { if (isPlainObject(src)) { const keys = Object.keys(src); return new HashMap( map((k) => [k, src[k]], keys), { cap: keys.length / (opts.load || DEFAULT_LOAD), ...opts } ); } else { return new HashMap(src, opts); } } export { HashMap, defHashMap };