Created
July 10, 2017 22:19
-
-
Save goldhand/1cfbd7a8109c61bbb147e3d0e4bd5b46 to your computer and use it in GitHub Desktop.
A map object that swings both ways ;)
This file contains hidden or 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
/** | |
* @module {Function} utils/bimap | |
* @flow | |
*/ | |
type MapKeyType = typeof BiMap.FORWARD | typeof BiMap.REVERSE; | |
// always sets Array<value> | |
// merges instead of overwrites by default | |
export default class BiMap<K, V> { | |
stores: { | |
[mapKey: MapKeyType]: Map<K, Array<V>>, | |
} | |
static FORWARD: 'FORWARD' = 'FORWARD'; | |
static REVERSE: 'REVERSE' = 'REVERSE'; | |
constructor() { | |
Object.defineProperty(this, 'stores', { | |
enumerable: false, | |
configurable: false, | |
writable: false, | |
value: { | |
[BiMap.FORWARD]: new Map(), | |
[BiMap.REVERSE]: new Map(), | |
}, | |
}); | |
Object.freeze(this.stores); | |
} | |
get forward(): * { | |
return this.mapFacade(BiMap.FORWARD); | |
} | |
get reverse(): * { | |
return this.mapFacade(BiMap.REVERSE); | |
} | |
mapFacade(mapKey: MapKeyType): * { | |
return { | |
get: this.mapGet(mapKey), | |
has: this.mapHas(mapKey), | |
keys: this.mapKeys(mapKey), | |
values: this.mapValues(mapKey), | |
entries: this.mapEntries(mapKey), | |
clear: this.mapClear(mapKey), | |
set: this.mapSet(mapKey), | |
delete: this.mapDelete(mapKey), | |
remove: this.mapRemove(mapKey), | |
size: this.mapSize(mapKey), | |
}; | |
} | |
// Map facade methods | |
mapGet = (mapKey: MapKeyType) => (key: K): Array<V> => this.stores[mapKey].get(key) || []; | |
mapHas = (mapKey: MapKeyType) => (key: K): boolean => this.stores[mapKey].has(key); | |
mapKeys = (mapKey: MapKeyType) => () => this.stores[mapKey].keys(); | |
mapValues = (mapKey: MapKeyType) => () => this.stores[mapKey].values(); | |
mapEntries = (mapKey: MapKeyType) => () => this.stores[mapKey].entries(); | |
mapClear = (mapKey: MapKeyType) => () => this.stores[mapKey].clear(); | |
mapSize = (mapKey: MapKeyType) => () => this.stores[mapKey].size; | |
mapSet = (mapKey: MapKeyType) => | |
/** | |
* Set a value on the forwardMap | |
* | |
* Map will always save value into an array. By default, values are merged | |
* with any existing values for the provided key (if they exist). | |
* | |
* @alias {Function} mapSet | |
* @param {*} key Key to set on forwardMap | |
* @param {*} value Value mapped to forwardMap[key] | |
* @param {boolean} [merge = true] Should value be merged into an | |
* existing value for provided key | |
* @returns {Map} The forwardMap | |
*/ | |
(key: K, value: V, merge: boolean = true): Map<K, Array<V>> => { | |
if (!merge) return this.stores[mapKey].set(key, [value]); | |
return this.stores[mapKey].set(key, [...this.mapGet(mapKey)(key), value]); | |
} | |
mapDelete = (mapKey: MapKeyType) => | |
(key: K): boolean => this.stores[mapKey].delete(key); | |
mapRemove = (mapKey: MapKeyType) => (key: K, value: V): boolean => { | |
const hasKey = this.mapHas(mapKey)(key); | |
if (hasKey) { | |
this.mapSet(mapKey)(key, this.mapGet(mapKey)(key).filter(v => v !== value)); | |
} | |
return hasKey; // return boolean | |
} | |
set(key: K, value: V, mergeForward: boolean = true, mergeReverse: boolean = true): BiMap<K, V> { | |
this.forward.set(key, value, mergeForward); | |
this.reverse.set(value, key, mergeReverse); | |
return this; | |
} | |
// TODO: cool idea but this doesn't make sense really. | |
related(key: K): Array<Array<V>> { | |
if (this.reverse.has(key)) return this.reverse.get(key).map(k => this.forward.get(k)); | |
if (this.forward.has(key)) return this.forward.get(key).map(k => this.reverse.get(k)); | |
// if key doesn't exist return Array<Array<>> | |
return [[]]; | |
} | |
/** | |
* Check if either map has(key) | |
* @param {*} key - key to check | |
* @returns {boolean} - if either forward or reverse have provided key | |
*/ | |
has(key: K): boolean { | |
return this.reverse.has(key) || this.forward.has(key); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment