Skip to content

Instantly share code, notes, and snippets.

@marlun78
Last active August 29, 2015 14:06
Show Gist options
  • Save marlun78/07be3b7b46a046886736 to your computer and use it in GitHub Desktop.
Save marlun78/07be3b7b46a046886736 to your computer and use it in GitHub Desktop.
Provides an immutable enum-like type where both keys and values must be unique.
/**
* UniqueMap.js
* Provides an immutable enum-like type where both keys and values must be unique.
* Copyright (c) 2014 marlun78
* MIT License, https://gist.github.com/marlun78/bd0800cf5e8053ba9f83
*/
(function (undefined) {
'use strict';
/**
* Immutable UniqueMap Type
* @typedef {Object} UniqueMap
* @property {Function} getKeys
* @property {Function} getValues
* @property {Function} hasKeys
* @property {Function} hasValues
*/
/**
* @constructor
* @param {Object} map - A map of unique keys and unique values.
* Keys must be strings and values must be primitives.
* @returns {UniqueMap}
*/
function UniqueMap(map) {
var keys = Object.keys(map),
values = [];
// Its OK with a loop here.
// This type is a convenience and is not expected to have high usage.
keys.forEach(function (key) {
var value = map[key],
type = typeof value;
if (type !== 'boolean' && type !== 'number' && type !== 'string') {
throw new TypeError('Values must be primitive values (Boolean, Number or String)');
}
if (keys.indexOf(value) > -1 || values.indexOf(value) > -1) {
throw new Error('Keys and values must be unique. Evaluating key/value ' + value);
}
values.push(value);
});
// Privileged methods (rather than shared prototype methods) to have privacy and immutability.
Object.defineProperties(this, {
getKey: {
/**
* Retrieves the key for a value
* @param {Boolean|Number|String} value - The value of the key to get
* @returns {String|Undefined}
*/
value: function getKey(value) {
var index = values.indexOf(value);
return index > -1 ? keys[index] : undefined;
}
},
getValue: {
/**
* Retrieves the value for a key
* @param {Boolean|Number|String} key - The key of the value to get
* @returns {Boolean|Number|String|Undefined}
*/
value: function getValue(key) {
var index = keys.indexOf(String(key));
return index > -1 ? values[index] : undefined;
}
},
hasKey: {
/**
* Checks if the passed key exists or not
* @param {Boolean|Number|String} key - The key of the value to get
* @returns {Boolean}
*/
value: function hasKey(key) {
var index = keys.indexOf(String(key));
return index > -1;
}
},
hasValue: {
/**
* Checks if the passed value exists or not
* @param {Boolean|Number|String} value - The value of the key to get
* @returns {Boolean}
*/
value: function hasValue(value) {
var index = values.indexOf(value);
return index > -1;
}
}
});
}
// ============================== TESTS ==============================
var colors = new UniqueMap({
red: '#f00',
green: '#0f0',
blue: '#00f'
});
console.log('Colors Tests');
console.assert(colors.getKey('#f00') === 'red', '`getValue` should return the value associated with the key');
console.assert(colors.getKey('none') === undefined, '`getKey` expected to return undefined if no key found');
console.assert(colors.hasKey('blue') === true, '`hasKey` expected to return true');
console.assert(colors.hasKey('none') === false, '`hasKey` expected to return false');
console.assert(colors.getValue('red') === '#f00', '`getValue` should return the value associated with the key');
console.assert(colors.getValue('none') === undefined, '`getValue` should return undefined if no key found');
console.assert(colors.hasValue('#00f') === true, '`hasKey` expected to return true');
console.assert(colors.hasValue('none') === false, '`hasKey` expected to return false');
try {
new UniqueMap({ fn: function(){} });
console.assert(false, 'Expected `new UniqueMap` to throw due to value of wrong type');
} catch(e) {}
try {
new UniqueMap({ a: 1, b: 1 });
console.assert(false, 'Expected `new UniqueMap` to throw due to non-unique values');
} catch(e) {}
console.dir(colors);
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment