@thi.ng/bidir-index
Version:
Bi-directional index mapping arbitrary keys to numeric IDs & vice versa
228 lines (227 loc) • 5.14 kB
JavaScript
class BidirIndex {
fwd;
rev;
nextID;
constructor(keys, opts = {}) {
this.nextID = opts.start || 0;
this.fwd = opts.map || /* @__PURE__ */ new Map();
this.rev = /* @__PURE__ */ new Map();
keys && this.addAll(keys);
}
get size() {
return this.fwd.size;
}
/**
* Yields same result as {@link BidirIndex.entries}.
*/
[Symbol.iterator]() {
return this.entries();
}
/**
* Returns iterator of `[key,id]` pairs.
*/
entries() {
return this.fwd.entries();
}
/**
* Returns iterator of all indexed keys.
*/
keys() {
return this.fwd.keys();
}
/**
* Returns iterator of all indexed IDs.
*/
values() {
return this.fwd.values();
}
/**
* Returns true if given `key` is known/indexed.
*
* @param key
*/
has(key) {
return this.fwd.has(key);
}
/**
* Returns true if given `id` has a corresponding known/indexed key.
*
* @param id
*/
hasID(id) {
return this.rev.has(id);
}
/**
* Reverse lookup of {@link BidirIndex.getID}. Returns the matching ID for
* given `key` or undefined if the key is not known.
*
* @param key
*/
get(key) {
return this.fwd.get(key);
}
/**
* Reverse lookup of {@link BidirIndex.get}. Returns the matching key for
* given `id` or undefined if the ID is not known.
*
* @param id
*/
getID(id) {
return this.rev.get(id);
}
/**
* Indexes given `key` and assigns & returns a new ID. If `key` is already
* known/indexed, returns its existing ID.
*
* @param key
*/
add(key) {
let id = this.fwd.get(key);
if (id === void 0) {
this.fwd.set(key, this.nextID);
this.rev.set(this.nextID, key);
id = this.nextID++;
}
return id;
}
/**
* Batch version of {@link BidirIndex.add}. Indexes all given keys and
* returns array of their corresponding IDs.
*
* @param keys
*/
addAll(keys) {
const res = [];
for (let k of keys) {
res.push(this.add(k));
}
return res;
}
/**
* Similar to {@link BidirIndex.addAll}, but returns indexed key IDs as ES6
* Set, thereby removing any duplicates present in `keys`.
*
* @param keys
*/
addAllUnique(keys) {
const res = /* @__PURE__ */ new Set();
for (let k of keys) {
res.add(this.add(k));
}
return res;
}
/**
* Removes bi-directional mapping for given `key` from the index. Returns
* true if successful.
*
* @param key
*/
delete(key) {
return __delete(this.fwd, this.rev, key);
}
/**
* Removes bi-directional mapping for given `id` from the index. Returns
* true if successful.
*
* @param id
*/
deleteID(id) {
return __delete(this.rev, this.fwd, id);
}
/**
* Batch version of {@link BidirIndex.delete}.
*
* @param keys
*/
deleteAll(keys) {
for (let k of keys) this.delete(k);
}
/**
* Batch version of {@link BidirIndex.deleteID}.
*
* @param ids
*/
deleteAllIDs(ids) {
for (let id of ids) this.deleteID(id);
}
/**
* Returns array of IDs for all given keys. If `fail` is true (default:
* false), throws error if any of the given keys is unknown/unindexed (use
* {@link BidirIndex.add} or {@link BidirIndex.addAll} first).
*
* @param keys
* @param fail
*/
getAll(keys, fail = false) {
return __iterate(this.fwd, keys, fail);
}
/**
* Similar to {@link BidirIndex.getAll}, but returns result as ES6 Set,
* thereby removing any duplicates in `keys`.
*
* @param keys
* @param fail
*/
getAllUnique(keys, fail = false) {
return new Set(__iterate(this.fwd, keys, fail));
}
/**
* Returns array of matching keys for all given IDs. If `fail` is true
* (default: false), throws error if any of the given IDs is
* unknown/unindexed (use {@link BidirIndex.add} or
* {@link BidirIndex.addAll} first).
*
* @param ids
* @param fail
*/
getAllIDs(ids, fail = false) {
return __iterate(this.rev, ids, fail);
}
/**
* Returns a compact JSON serializable version of the index. Use
* {@link bidirIndexFromJSON} to instantiate an index from such a JSON
* serialization.
*/
toJSON() {
return {
pairs: [...this.entries()],
nextID: this.nextID
};
}
}
const __delete = (fwd, rev, key) => {
const val = fwd.get(key);
if (val !== void 0) {
fwd.delete(key);
rev.delete(val);
return true;
}
return false;
};
const __iterate = (index, keys, fail) => {
const res = [];
for (let k of keys) {
const val = index.get(k);
if (val === void 0) {
if (fail) throw new Error(`unknwon key/ID: ${k}`);
} else {
res.push(val);
}
}
return res;
};
const defBidirIndex = (keys, opts) => new BidirIndex(keys, opts);
const bidirIndexFromJSON = (src, map) => {
const $src = typeof src === "string" ? JSON.parse(src) : src;
const res = new BidirIndex(null, { map, start: $src.nextID });
$src.pairs.forEach(([k, id]) => {
res.fwd.set(k, id);
res.rev.set(id, k);
});
return res;
};
export {
BidirIndex,
bidirIndexFromJSON,
defBidirIndex
};