Created
March 29, 2019 16:06
-
-
Save webstrand/97412fe5bd689b61305e8f94ec6b9c45 to your computer and use it in GitHub Desktop.
Finitely recursive map types using arrays of values as keys. !!FRAGILE!! Don't use any[] for key type, expect heisenbugs.
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
export type MapTree<K extends readonly [unknown, ...unknown[]], V> = { | |
0: Map<K[number], V>, | |
1: ((..._: K) => any) extends ((_: infer Head, ...__: infer Tail) => any) | |
? Tail extends readonly [unknown, ...unknown[]] | |
? Map<Head, MapTree<Tail, V>> | |
: never | |
: never | |
}[K extends readonly [unknown] ? 0 : 1] & { | |
setKey(key: Readonly<K>, value: V): void; | |
getKey(key: Readonly<K>): V | undefined; | |
hasKey(key: Readonly<K>): boolean; | |
deleteKey(key: Readonly<K>): boolean; | |
}; | |
export type ReadonlyMapTree<K extends readonly [unknown, ...unknown[]], V> = { | |
0: ReadonlyMap<K[number], V>, | |
1: ((..._: K) => any) extends ((_: infer Head, ...__: infer Tail) => any) | |
? Tail extends readonly [unknown, ...unknown[]] | |
? ReadonlyMap<Head, ReadonlyMapTree<Tail, V>> | |
: never | |
: never | |
}[K extends readonly [unknown] ? 0 : 1] & { | |
getKey(key: Readonly<K>): V | undefined; | |
hasKey(key: Readonly<K>): boolean; | |
}; | |
export class MapTreeImpl<K extends readonly [unknown, ...unknown[]], V> extends Map<any, any> { | |
setKey(key: Readonly<K>, value: V): void; | |
setKey(key: readonly any[], value: V): void { | |
let cursor: Map<any, any> = this; | |
let i = 0; | |
let keylen = key.length - 1; | |
for(; i < keylen; i += 1) { | |
let keypart = key[i]; | |
let next = cursor.get(keypart); | |
if(typeof next === "undefined") cursor.set(keypart, next = new Map()); | |
cursor = next; | |
} | |
cursor.set(key[i], value); | |
} | |
getKey(key: Readonly<K>): V | undefined; | |
getKey(key: readonly any[]): V | undefined { | |
let cursor: Map<any, any> = this; | |
let i = 0; | |
let keylen = key.length - 1; | |
for(; i < keylen; i += 1) { | |
let keypart = key[i]; | |
let next = cursor.get(keypart); | |
if(typeof next === "undefined") return; | |
cursor = next; | |
} | |
return cursor.get(key[i]); | |
} | |
hasKey(key: Readonly<K>): boolean; | |
hasKey(key: readonly any[]): boolean { | |
let cursor: Map<any, any> = this; | |
let i = 0; | |
let keylen = key.length - 1; | |
for(; i < keylen; i += 1) { | |
let keypart = key[i]; | |
let next = cursor.get(keypart); | |
if(typeof next === "undefined") return false; | |
cursor = next; | |
} | |
return cursor.has(key[i]); | |
} | |
deleteKey(key: Readonly<K>): boolean; | |
deleteKey(key: readonly any[]): boolean { | |
let cursor: Map<any, any> = this; | |
let i = 0; | |
let keylen = key.length - 1; | |
let path = []; | |
for(; i < keylen; i += 1) { | |
let keypart = key[i]; | |
let next = cursor.get(keypart); | |
if(typeof next === "undefined") return false; | |
path.push(cursor); | |
cursor = next; | |
} | |
if(!cursor.delete(key[i--])) return false; | |
for(; cursor.size === 0 && i >= 0; i -= 1) { | |
let prev = path.pop()!; | |
prev.delete(key[i]); | |
cursor = prev; | |
} | |
return true; | |
} | |
} | |
export const MapTree: { | |
prototype: Map<any, any>; | |
new <K extends readonly [unknown, ...unknown[]], V>(): MapTree<K, V>; | |
} = MapTreeImpl as any; | |
export type MultiMapTree<K extends readonly [unknown, ...unknown[]], V> = { | |
0: Map<K[number], Set<V>>, | |
1: ((..._: K) => any) extends ((_: infer Head, ...__: infer Tail) => any) | |
? Tail extends readonly [unknown, ...unknown[]] | |
? Map<Head, MultiMapTree<Tail, V>> | |
: never | |
: never | |
}[K extends readonly [unknown] ? 0 : 1] & { | |
addKey(key: Readonly<K>, value: V): boolean; | |
getKey(key: Readonly<K>): ReadonlySet<V>; | |
hasKey(key: Readonly<K>, value?: Readonly<V>): boolean; | |
deleteKey(key: Readonly<K>, value?: Readonly<V>): boolean; | |
}; | |
export type ReadonlyMultiMapTree<K extends readonly [unknown, ...unknown[]], V> = { | |
0: ReadonlyMap<K[number], ReadonlySet<V>>, | |
1: ((..._: K) => any) extends ((_: infer Head, ...__: infer Tail) => any) | |
? Tail extends readonly [unknown, ...unknown[]] | |
? ReadonlyMap<Head, ReadonlyMultiMapTree<Tail, V>> | |
: never | |
: never | |
}[K extends readonly [unknown] ? 0 : 1] & { | |
getKey(key: readonly any[]): ReadonlySet<V>; | |
hasKey(key: readonly any[], value?: Readonly<V>): boolean; | |
}; | |
export class MultiMapTreeImpl<K extends readonly [unknown, ...unknown[]], V> extends Map<any, any> { | |
static readonly Empty: ReadonlySet<any> = new Set(); | |
addKey(key: Readonly<K>, value: V): boolean; | |
addKey(key: readonly any[], value: V): boolean { | |
let cursor: Map<any, any> = this; | |
let i = 0; | |
let keylen = key.length - 1; | |
for(; i < keylen; i += 1) { | |
let keypart = key[i]; | |
let next = cursor.get(keypart); | |
if(typeof next === "undefined") cursor.set(keypart, next = new Map()); | |
cursor = next; | |
} | |
let keypart = key[i]; | |
let next: Set<V> = cursor.get(keypart); | |
if(typeof next === "undefined") cursor.set(keypart, next = new Set()); | |
if(next.has(value)) return false; | |
next.add(value); | |
return true; | |
} | |
getKey(key: Readonly<K>): ReadonlySet<V>; | |
getKey(key: readonly any[]): ReadonlySet<V> { | |
let cursor: Map<any, any> = this; | |
let i = 0; | |
let keylen = key.length - 1; | |
for(; i < keylen; i += 1) { | |
let keypart = key[i]; | |
let next = cursor.get(keypart); | |
if(typeof next === "undefined") return MultiMapTree.Empty; | |
cursor = next; | |
} | |
let keypart = key[i]; | |
let next: Set<V> = cursor.get(keypart); | |
if(typeof next === "undefined") return MultiMapTree.Empty; | |
return next; | |
} | |
hasKey(key: Readonly<K>, value?: Readonly<V>): boolean; | |
hasKey(key: readonly any[], value?: Readonly<V>): boolean { | |
let cursor: Map<any, any> = this; | |
let i = 0; | |
let keylen = key.length - 1; | |
for(; i < keylen; i += 1) { | |
let keypart = key[i]; | |
let next = cursor.get(keypart); | |
if(typeof next === "undefined") return false; | |
cursor = next; | |
} | |
let keypart = key[i]; | |
let next: Set<V> = cursor.get(keypart); | |
if(typeof next === "undefined") return false; | |
return arguments.length > 1 ? next.has(key[i]) : true; | |
} | |
deleteKey(key: Readonly<K>, value?: Readonly<V>): boolean; | |
deleteKey(key: readonly any[], value?: Readonly<V>): boolean { | |
let cursor: Map<any, any> = this; | |
let i = 0; | |
let keylen = key.length - 1; | |
let path = []; | |
for(; i < keylen; i += 1) { | |
let keypart = key[i]; | |
let next = cursor.get(keypart); | |
if(typeof next === "undefined") return false; | |
path.push(cursor); | |
cursor = next; | |
} | |
if(typeof value !== "undefined") { | |
let keypart = key[i]; | |
let next = cursor.get(keypart); | |
if(typeof next === undefined) return false; | |
path.push(cursor); | |
cursor = next; | |
if(!cursor.delete(value)) return false; | |
} | |
else { | |
if(!cursor.delete(key[i--])) return false; | |
} | |
for(; cursor.size === 0 && i >= 0; i -= 1) { | |
let prev = path.pop()!; | |
prev.delete(key[i]); | |
cursor = prev; | |
} | |
return true; | |
} | |
}; | |
export const MultiMapTree: { | |
prototype: Map<any, Set<any>>; | |
new <K extends readonly [unknown, ...unknown[]], V>(): MultiMapTree<K, V>; | |
readonly Empty: ReadonlySet<any>; | |
} = MultiMapTreeImpl as any; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment