Skip to content

Instantly share code, notes, and snippets.

@rpggio
Created October 27, 2020 02:39
Show Gist options
  • Save rpggio/b8fa9f28fb16e9e2c9bb67c5dce1a262 to your computer and use it in GitHub Desktop.
Save rpggio/b8fa9f28fb16e9e2c9bb67c5dce1a262 to your computer and use it in GitHub Desktop.
Generic Map with complex key, in TypeScript
export function ComplexKeyedMap<K, V>(
formatter: StringFormatter<K>,
entries?: readonly (readonly [K, V])[] | null
): Map<K, V> {
const map = new Map<string, V>(
entries?.map(([k, v]) => [formatter.asString(k), v])
)
function* getEntries() {
for (const [strKey, value] of map) {
yield [formatter.fromString(strKey), value] as [K, V]
}
}
const typedMap: Map<K, V> = {
clear: map.clear,
delete(key: K) {
return map.delete(formatter.asString(key))
},
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any) {
map.forEach((value, key) =>
callbackfn(value, formatter.fromString(key), typedMap), thisArg)
},
get(key: K) {
return map.get(formatter.asString(key))
},
has(key: K) {
return map.has(formatter.asString(key))
},
set(key: K, value: V) {
map.set(formatter.asString(key), value)
return typedMap
},
get size() {
return map.size
},
[Symbol.iterator]: getEntries,
entries: getEntries,
*keys() {
for (const [strKey] of map) {
yield formatter.fromString(strKey)
}
},
*values() {
for (const [, value] of map) {
yield value
}
},
[Symbol.toStringTag]: map[Symbol.toStringTag]
}
return typedMap
}
export type StringFormatter<T> = {
asString: (t: T) => string,
fromString: (value: string) => T
}
import { ComplexKeyedMap, StringFormatter } from './complexKeyedMap'
type Point = [number, number]
const nullPoint: Point = [NaN, NaN]
export function PointMap<V>(entries?: readonly (readonly [Point, V])[] | null) {
return ComplexKeyedMap(keyFormatter, entries)
}
const keyFormatter: StringFormatter<Point> = {
asString(point: Point) {
return point.map(paddedUnit).join(',')
},
fromString(literal: string): Point {
if (!literal) {
return nullPoint
}
const parts = literal.split(',')
if (parts.length !== 2) {
return nullPoint
}
return parts.map(it => Number(it)) as Point
}
}
function paddedUnit(unit: number) {
return unit.toString().padStart(5)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment