buckets-js
Version:
Buckets is a complete, fully tested and documented data structure library written in pure JavaScript.
211 lines (196 loc) • 7 kB
JavaScript
/**
* Creates an empty dictionary.
* @class <p>Dictionaries map keys to values, each key can map to at most one value.
* This implementation accepts any kind of objects as keys.</p>
*
* <p>If the keys are custom objects, a function that converts keys to unique
* strings must be provided at construction time.</p>
* <p>Example:</p>
* <pre>
* function petToString(pet) {
* return pet.name;
* }
* </pre>
* @constructor
* @param {function(Object):string=} toStrFunction Optional function used
* to convert keys to unique strings. If the keys aren't strings or if toString()
* is not appropriate, a custom function which receives a key and returns a
* unique string must be provided.
*/
buckets.Dictionary = function (toStrFunction) {
/**
* @exports dictionary as buckets.Dictionary
* @private
*/
var dictionary = {},
// Object holding the key-value pairs.
table = {},
// Number of keys in the dictionary.
nElements = 0,
// Function to convert keys unique to strings.
toStr = toStrFunction || buckets.defaultToString,
// Special string to prefix keys and avoid name collisions with existing properties.
keyPrefix = '/$ ';
/**
* Returns the value associated with the specified key in the dictionary.
* @param {Object} key The key.
* @return {*} The mapped value or
* undefined if the dictionary contains no mapping for the provided key.
*/
dictionary.get = function (key) {
var pair = table[keyPrefix + toStr(key)];
if (buckets.isUndefined(pair)) {
return undefined;
}
return pair.value;
};
/**
* Associates the specified value with the specified key in the dictionary.
* If the dictionary previously contained a mapping for the key, the old
* value is replaced by the specified value.
* @param {Object} key The key.
* @param {Object} value Value to be mapped with the specified key.
* @return {*} Previous value associated with the provided key, or undefined if
* there was no mapping for the key or the key/value is undefined.
*/
dictionary.set = function (key, value) {
var ret, k, previousElement;
if (buckets.isUndefined(key) || buckets.isUndefined(value)) {
return undefined;
}
k = keyPrefix + toStr(key);
previousElement = table[k];
if (buckets.isUndefined(previousElement)) {
nElements += 1;
ret = undefined;
} else {
ret = previousElement.value;
}
table[k] = {
key: key,
value: value
};
return ret;
};
/**
* Removes the value associated with the specified key from the dictionary if it exists.
* @param {Object} key The key.
* @return {*} Removed value associated with the specified key, or undefined if
* there was no mapping for the key.
*/
dictionary.remove = function (key) {
var k = keyPrefix + toStr(key),
previousElement = table[k];
if (!buckets.isUndefined(previousElement)) {
delete table[k];
nElements -= 1;
return previousElement.value;
}
return undefined;
};
/**
* Returns an array containing all the keys in the dictionary.
* @return {Array} An array containing all the keys in the dictionary.
*/
dictionary.keys = function () {
var array = [],
name;
for (name in table) {
if (Object.prototype.hasOwnProperty.call(table, name)) {
array.push(table[name].key);
}
}
return array;
};
/**
* Returns an array containing all the values in the dictionary.
* @return {Array} An array containing all the values in the dictionary.
*/
dictionary.values = function () {
var array = [],
name;
for (name in table) {
if (Object.prototype.hasOwnProperty.call(table, name)) {
array.push(table[name].value);
}
}
return array;
};
/**
* Executes the provided function once per key-value pair
* present in the dictionary.
* @param {function(Object,Object):*} callback Function to execute. Receives
* 2 arguments: key and value. To break the iteration you can
* optionally return false inside the callback.
*/
dictionary.forEach = function (callback) {
var name, pair, ret;
for (name in table) {
if (Object.prototype.hasOwnProperty.call(table, name)) {
pair = table[name];
ret = callback(pair.key, pair.value);
if (ret === false) {
return;
}
}
}
};
/**
* Returns true if the dictionary contains a mapping for the specified key.
* @param {Object} key The key.
* @return {boolean} True if the dictionary contains a mapping for the
* specified key.
*/
dictionary.containsKey = function (key) {
return !buckets.isUndefined(dictionary.get(key));
};
/**
* Removes all keys and values from the dictionary.
* @this {buckets.Dictionary}
*/
dictionary.clear = function () {
table = {};
nElements = 0;
};
/**
* Returns the number of key-value pais in the dictionary.
* @return {number} The number of key-value mappings in the dictionary.
*/
dictionary.size = function () {
return nElements;
};
/**
* Returns true if the dictionary contains no keys.
* @return {boolean} True if this dictionary contains no mappings.
*/
dictionary.isEmpty = function () {
return nElements <= 0;
};
/**
* Returns true if the dictionary is equal to another dictionary.
* Two dictionaries are equal if they have the same key-value pairs.
* @param {buckets.Dictionary} other The other dictionary.
* @param {function(Object,Object):boolean=} equalsFunction Optional
* function to check if two values are equal. If the values in the dictionaries
* are custom objects you should provide a custom equals function, otherwise
* the === operator is used to check equality between values.
* @return {boolean} True if the dictionary is equal to the given dictionary.
*/
dictionary.equals = function (other, equalsFunction) {
var eqf, isEqual;
if (buckets.isUndefined(other) || typeof other.keys !== 'function') {
return false;
}
if (dictionary.size() !== other.size()) {
return false;
}
eqf = equalsFunction || buckets.defaultEquals;
isEqual = true;
other.forEach(function (k, v) {
isEqual = eqf(dictionary.get(k), v);
return isEqual;
});
return isEqual;
};
return dictionary;
};