Skip to content

Instantly share code, notes, and snippets.

@webstrand
Created March 29, 2019 16:06
Show Gist options
  • Save webstrand/97412fe5bd689b61305e8f94ec6b9c45 to your computer and use it in GitHub Desktop.
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.
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