@thi.ng/associative
Version:
ES Map/Set-compatible implementations with customizable equality semantics & supporting operations
231 lines (230 loc) • 6.8 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 _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 (let p of __privateGet(this, _bins)) {
if (p) yield [p[0], p[1]];
}
}
*keys() {
for (let p of __privateGet(this, _bins)) {
if (p) yield p[0];
}
}
*values() {
for (let 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 (let 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 (let 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
};