UNPKG

data-structures

Version:

JavaScript data structures written in CoffeeScript.

144 lines (122 loc) 4.49 kB
### Kind of a stopgap measure for the upcoming [JavaScript Map](http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets) **Note:** due to JavaScript's limitations, hashing something other than Boolean, Number, String, Undefined, Null, RegExp, Function requires a hack that inserts a hidden unique property into the object. This means `set`, `get`, `has` and `delete` must employ the same object, and not a mere identical copy as in the case of, say, a string. ## Overview example: ```js var map = new Map({'alice': 'wonderland', 20: 'ok'}); map.set('20', 5); // => 5 map.get('20'); // => 5 map.has('alice'); // => true map.delete(20) // => true var arr = [1, 2]; map.add(arr, 'goody'); // => 'goody' map.has(arr); // => true map.has([1, 2]); // => false. Needs to compare by reference map.forEach(function(key, value) { console.log(key, value); }); ``` ## Properties: - size: The total number of `(key, value)` pairs. ### # TODO: maybe change the hashing method for arrays and objects to something less # hacky. We can recursively dig out their properties' value and join them, but # that'd make it O(n). # For hashing special types, e.g. objects, arrays and dates. SPECIAL_TYPE_KEY_PREFIX = '_mapId_' class Map # Class variable and method. @_mapIdTracker: 0 @_newMapId: -> @_mapIdTracker++ constructor: (objectToMap)-> ### Pass an optional object whose (key, value) pair will be hashed. **Careful** not to pass something like {5: 'hi', '5': 'hello'}, since JavaScript's native object behavior will crush the first 5 property before it gets to constructor. ### # _content is composed of (key, value) pairs where `value` itself is an # array of two elements: first being the actual value to store, second being # the original key, displayed for iteration. @_content = {} # Used to track objects and arrays. @_itemId = 0 @_id = Map._newMapId() @size = 0 @set key, value for own key, value of objectToMap # Public. Allow user-defined hash function. hash: (key, makeHash = no) -> ### The hash function for hashing keys is public. Feel free to replace it with your own. The `makeHash` parameter is optional and accepts a boolean (defaults to `false`) indicating whether or not to produce a new hash (for the first use, naturally). _Returns:_ the hash. ### # [object typeExtracted]. type = _extractDataType key # Obscure hack to add a secret property to the object, used as key for hash # map. Reason for doing so on array: [obj1, obj2] would have the same hash # as [obj3, obj4]. if _isSpecialType key propertyForMap = SPECIAL_TYPE_KEY_PREFIX + @_id if makeHash and not key[propertyForMap] key[propertyForMap] = @_itemId++ # Format: '_hashMapId' return propertyForMap + '_' + key[propertyForMap] else return type + '_' + key set: (key, value) -> ### _Returns:_ value. ### if not @has key then @size++ @_content[@hash(key, yes)] = [value, key] return value get: (key) -> ### _Returns:_ value corresponding to the key, or undefined if not found. ### @_content[@hash key]?[0] has: (key) -> ### Check whether a value exists for the key. _Returns:_ true or false. ### @hash(key) of @_content delete: (key) -> ### Remove the (key, value) pair. _Returns:_ **true or false**. Unlike most of this library, this method doesn't return the deleted value. This is so that it conforms to the future JavaScript `map.delete()`'s behavior. ### hashedKey = @hash key if hashedKey of @_content delete @_content[hashedKey] if _isSpecialType key then delete key[SPECIAL_TYPE_KEY_PREFIX + @_id] @size-- return true return false forEach: (operation) -> ### Traverse through the map. Pass a function of the form `fn(key, value)`. _Returns:_ undefined. ### operation(value[1], value[0]) for key, value of @_content # Manual return to avoid CoffeeScript accumulating an array for return. return _isSpecialType = (key) -> simpleHashableTypes = ['Boolean', 'Number', 'String', 'Undefined', 'Null', 'RegExp', 'Function'] type = _extractDataType key for simpleType in simpleHashableTypes if type is simpleType then return no return yes _extractDataType = (type) -> Object.prototype.toString.apply(type).match(/\[object (.+)\]/)[1] module.exports = Map