Created
May 12, 2022 19:20
-
-
Save Tyriar/dada94b6e8a10f60b67a38a2dd205925 to your computer and use it in GitHub Desktop.
A map where the value is accessed via 2 independent keys
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface IDualKeyMap<K, V> { | |
clear(): void; | |
delete(keyA: K, keyB: K): boolean; | |
forEach(callbackfn: (value: V, keyA: K, keyB: K, map: IDualKeyMap<K, V>) => void, thisArg?: any): void; | |
get(keyA: K, keyB: K): V | undefined; | |
has(keyA: K, keyB: K): boolean; | |
set(keyA: K, keyB: K, value: V): this; | |
readonly size: number; | |
} | |
/** | |
* A Map where the value is accessed via 2 independent keys. This essentially wraps a | |
* `Map<K, Map<K, V>>` and makes it more convenient to use. | |
*/ | |
export class DualKeyMap<K, V> implements IDualKeyMap<K, V> { | |
private _data: Map<K, Map<K, V>> = new Map(); | |
private _ttlTimeout: number | undefined; | |
public get size(): number { | |
let result = 0; | |
for (const inner of this._data.values()) { | |
result += inner.size; | |
} | |
return result; | |
} | |
/** | |
* @param _ttlMs An optional amount of milliseconds to wait to clear the map, debounced after | |
* every operation. | |
*/ | |
constructor( | |
private readonly _ttlMs?: number | |
) { | |
} | |
public clear(): void { | |
this._clearTtl(); | |
this._data.clear(); | |
} | |
public delete(keyA: K, keyB: K): boolean { | |
this._triggerTtl(); | |
const inner = this._data.get(keyA); | |
if (!inner) { | |
return false; | |
} | |
return inner.delete(keyB); | |
} | |
public forEach(callbackfn: (value: V, keyA: K, keyB: K, map: DualKeyMap<K, V>) => void, thisArg?: any): void { | |
this._triggerTtl(); | |
const fn = callbackfn.bind(thisArg ?? this); | |
for (const [keyA, inner] of this._data.entries()) { | |
for (const [keyB, value] of inner.entries()) { | |
fn(value, keyA, keyB, this); | |
} | |
} | |
} | |
public get(keyA: K, keyB: K): V | undefined { | |
this._triggerTtl(); | |
const inner = this._data.get(keyA); | |
if (!inner) { | |
return undefined; | |
} | |
return inner.get(keyB); | |
} | |
public has(keyA: K, keyB: K): boolean { | |
this._triggerTtl(); | |
const inner = this._data.get(keyA); | |
if (!inner) { | |
return false; | |
} | |
return inner.has(keyB); | |
} | |
public set(keyA: K, keyB: K, value: V): this { | |
this._triggerTtl(); | |
let inner = this._data.get(keyA); | |
if (!inner) { | |
inner = new Map(); | |
} | |
this._data.set(keyA, inner); | |
inner.set(keyB, value); | |
return this; | |
} | |
private _clearTtl(): void { | |
if (this._ttlTimeout !== undefined) { | |
clearTimeout(this._ttlTimeout); | |
this._ttlTimeout = undefined; | |
} | |
} | |
private _triggerTtl(): void { | |
if (this._ttlMs === undefined) { | |
return; | |
} | |
this._clearTtl(); | |
this._ttlTimeout = setTimeout(() => { | |
this.clear(); | |
}, this._ttlMs); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment