@ndn/util
Version:
NDNts: general utilities
178 lines (177 loc) • 4.36 kB
JavaScript
const emptySet = new Set();
/**
* Map that transforms keys.
* @typeParam K - Input key type.
* @typeParam V - Value type.
* @typeParam I - Indexable key type.
* @typeParam L - Lookup key type.
*/
export class KeyMap {
keyOf;
/**
* Constructor.
* @param keyOf - Function to transform input key to indexable key.
*/
constructor(keyOf) {
this.keyOf = keyOf;
}
m = new Map();
get size() { return this.m.size; }
has(key) {
return this.m.has(this.keyOf(key));
}
get(key) {
return this.m.get(this.keyOf(key))?.[1];
}
set(key, value) {
this.m.set(this.keyOf(key), [key, value]);
return this;
}
delete(key) {
return this.m.delete(this.keyOf(key));
}
[Symbol.iterator]() {
return this.m.values();
}
}
/**
* MultiMap that transforms keys.
* @typeParam K - Input key type.
* @typeParam V - Value type.
* @typeParam I - Indexable key type.
* @typeParam L - Lookup key type.
*/
export class KeyMultiMap {
/**
* Constructor.
* @param keyOf - Function to transform input key to indexable key.
*/
constructor(keyOf) {
this.m = new KeyMap(keyOf);
}
m;
size_ = 0;
/** Number of distinct keys. */
get dimension() { return this.m.size; }
/** Number of values. */
get size() { return this.size_; }
/** Count values associated with a key. */
count(key) {
return this.m.get(key)?.size ?? 0;
}
/** List values associated with a key. */
list(key) {
return this.m.get(key) ?? emptySet;
}
/**
* Add a key-value pair.
* Values are stored in a Set, so duplicates are skipped.
* @returns count(key) after the operation.
*/
add(key, value) {
let c = this.m.get(key);
if (!c) {
c = new Set();
this.m.set(key, c);
}
const n = c.size;
c.add(value);
this.size_ += c.size - n;
return c.size;
}
/**
* Remove a key-value pair.
* No-op if key-value does not exist.
* @returns `count(key)` after the operation.
*/
remove(key, value) {
const c = this.m.get(key);
if (!c) {
return 0;
}
const n = c.size;
c.delete(value);
this.size_ += c.size - n;
if (c.size === 0) {
this.m.delete(key);
}
return c.size;
}
/** Iterate over key and associated values. */
associations() {
return this.m[Symbol.iterator]();
}
/** Iterate over key-value pairs. */
*[Symbol.iterator]() {
for (const [key, values] of this.associations()) {
for (const value of values) {
yield [key, value];
}
}
}
}
/** Container that associates a key with multiple distinct values. */
export class MultiMap extends KeyMultiMap {
constructor() {
super((k) => k);
}
}
/**
* MultiSet that transforms keys.
* @typeParam K - Input key type.
* @typeParam I - Indexable key type.
* @typeParam L - Lookup key type.
*/
export class KeyMultiSet {
/**
* Constructor.
* @param keyOf - Function to transform input key to indexable key.
*/
constructor(keyOf) {
this.m = new KeyMap(keyOf);
}
m;
size_ = 0;
/** Number of distinct keys. */
get dimension() { return this.m.size; }
/** Number of values. */
get size() { return this.size_; }
/** Count occurrences of a key. */
count(key) {
return this.m.get(key) ?? 0;
}
/**
* Add a key.
* @returns Number of occurrences after the operation.
*/
add(key) {
const n = this.count(key) + 1;
this.m.set(key, n);
++this.size_;
return n;
}
/**
* Remove a key.
* No-op if key does not exist.
* @returns Number of occurrences after the operation.
*/
remove(key) {
let n = this.count(key);
if (n === 0) {
return n;
}
--this.size_;
--n;
if (n === 0) {
this.m.delete(key);
}
else {
this.m.set(key, n);
}
return n;
}
/** Iterate over key and number of occurrences. */
multiplicities() {
return this.m[Symbol.iterator]();
}
}