Skip to content

Commit cbb537b

Browse files
committed
JsonMap which can use json as a map key.
1 parent f5e5b37 commit cbb537b

File tree

1 file changed

+67
-0
lines changed

1 file changed

+67
-0
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/** Map which can take any JSON as key and uses its sorted serialization as the underlying key. */
2+
export class JsonMap<K, V> {
3+
private map = new Map<string, V>()
4+
5+
set(key: K, value: V): this {
6+
this.map.set(stableStringify(key), value)
7+
return this
8+
}
9+
10+
get(key: K): V | undefined {
11+
return this.map.get(stableStringify(key))
12+
}
13+
14+
has(key: K): boolean {
15+
return this.map.has(stableStringify(key))
16+
}
17+
18+
delete(key: K): boolean {
19+
return this.map.delete(stableStringify(key))
20+
}
21+
22+
clear(): void {
23+
this.map.clear()
24+
}
25+
26+
get size(): number {
27+
return this.map.size
28+
}
29+
30+
*values(): IterableIterator<V> {
31+
yield* this.map.values()
32+
}
33+
34+
*entries(): IterableIterator<[K, V]> {
35+
for (const [stringKey, value] of this.map.entries()) {
36+
yield [JSON.parse(stringKey) as K, value]
37+
}
38+
}
39+
40+
*keys(): IterableIterator<K> {
41+
for (const stringKey of this.map.keys()) {
42+
yield JSON.parse(stringKey) as K
43+
}
44+
}
45+
46+
[Symbol.iterator](): IterableIterator<[K, V]> {
47+
return this.entries()
48+
}
49+
}
50+
51+
function stableStringify(v: unknown): string {
52+
return JSON.stringify(v, (_k, val) => {
53+
if (val && typeof val === 'object' && !Array.isArray(val)) {
54+
// sort object keys recursively
55+
return Object.keys(val as Record<string, unknown>)
56+
.sort()
57+
.reduce(
58+
(acc, k) => {
59+
;(acc as any)[k] = (val as any)[k]
60+
return acc
61+
},
62+
{} as Record<string, unknown>,
63+
)
64+
}
65+
return val
66+
})
67+
}

0 commit comments

Comments
 (0)