Hash
A Hash
is an unordered collection of key-value pairs. It can be thought of
as a table that maps ‘key’ objects (of which there are no duplicates within a
Hash
) to ‘value’ objects (of which there may be duplicates). This
implementation is close to Ruby’s Hash
class, though you may be familiar
with the data structure in some other form; Java’s HashMap
, Python
dictionaries, JavaScript objects, PHP’s associative arrays and Scheme’s alists
all perform a similar function.
// In the browser JS.require('JS.Hash', function(Hash) { ... }); // In CommonJS var Hash = require('jsclass/src/hash').Hash;
JavaScript’s native Object
class could be considered a basic kind of
hashtable in which the keys must be strings. This class provides a more
general-purpose implementation with many helpful methods not provided by
JavaScript. The keys in a Hash
may be numbers, strings, or any object that
implements the equals()
and hash()
methods.
For our examples we’re going to use two classes with pretty trivial equality
operations. Note how hash()
uses the same data as equals()
ensuring
correct behaviour:
State = new Class({ initialize: function(name, code) { this.name = name; this.code = code; }, equals: function(other) { return (other instanceof this.klass) && other.code === this.code; }, hash: function() { return this.code; } }); Senator = new Class({ initialize: function(name) { this.name = name; }, equals: function(other) { return (other instanceof this.klass) && other.name === this.name; }, hash: function() { return this.name; } });
And we’ll instantiate a few pieces of data to put in a Hash
:
var NY = new State('New York', 'NY'), CA = new State('California', 'CA'), IL = new State('Illinois', 'IL'), TX = new State('Texas', 'TX'), VA = new State('Virginia', 'VA'), hutchinson = new Senator('Kay Bailey Hutchison'), burris = new Senator('Roland Burris'), feinstein = new Senator('Dianne Feinstein'), gillibrand = new Senator('Kirsten Gillibrand'), hancock = new Senator('John Hancock');
Instantiating a Hash
There are three ways to instantiate a Hash
. The first is to simply list the
key-value pairs as an array. Retrieving a key will then return the
corresponding value:
var senators = new Hash([ NY, gillibrand, CA, feinstein, IL, burris, TX, hutchinson ]); senators.get(IL).name // -> "Roland Burris"
One important function of a Hash
is that you don’t need the original key
object to retrieve its associated value, you just need some object equal to
the key. States are compared using their code
, so we could create another
object to represent Texas to get its senator:
senators.get(new State('Lone Star State', 'TX')) // -> #<Senator name="Kay Bailey Hutchison">
The second way is to instantiate the Hash
using a single default value; this
value will then be returned when you ask for a key the hash doesn’t have:
var senators = new Hash(hancock); senators.get(NY).name // -> "John Hancock"
The third and final way is to instantiate using a function, which will be called when a nonexistent key is accessed. The function is passed the hash and the requested key, so you can store the result in the hash if required:
var senators = new Hash(function(hash, key) { var result = new Senator('The senator for ' + key.name + ' (' + key.code + ')'); hash.store(key, result); return result; }); senators.size // -> 0 senators.get(CA).name // -> "The senator for California (CA)" senators.size // -> 1
Enumeration
Hashes are Enumerable
, and their forEach()
method
yields a Hash.Pair
object with each iteration. Each pair has a key
and a
value
. Iteration order is not guaranteed, though on many JavaScript
implementations you may find insertion order is preserved. Do not rely on
order when using a Hash
. For example:
var senators = new Hash([ NY, gillibrand, CA, feinstein, IL, burris, TX, hutchinson ]); senators.forEach(function(pair) { console.log(pair.key.code + ': ' + pair.value.name); }); // Prints: // NY: Kirsten Gillibrand // CA: Dianne Feinstein // IL: Roland Burris // TX: Kay Bailey Hutchison
The hash package also contains a class called OrderedHash
. It has the
same API as Hash
but keeps its keys in insertion order at all times.
The instance methods of Hash
are as follows:
assoc(key)
Returns the Hash.Pair
object corresponding to the given key
, or null
if
so such key is found.
senators.assoc(NY).key.code // -> "NY" senators.assoc(IL).value.name // -> "Roland Burris" senators.assoc(VA) // -> null
rassoc(value)
Returns the first matching Hash.Pair
object for to the given value
, or
null
if so such value is found.
senators.rassoc(feinstein).key.code // -> "CA" senators.rassoc(burris).value.name // -> "Roland Burris" senators.rassoc(hancock) // -> null
clear()
Removes all the key-value pairs from the hash.
senators.clear(); senators.size // -> 0 senators.get(TX) // -> null
compareByIdentity()
Instructs the hash to use the ===
identity operator instead of the equals()
method to compare keys. Values must then be retrieved using the same key object
as was used to store the value initially.
comparesByIdentity()
Returns true
iff the hash is using the ===
operator rather than the
equals()
method to compare keys.
setDefault(value)
Sets the default value for the hash to the given value
. The default value is
returned whenever a nonexistent key is accessed using get()
, remove()
or
shift()
. The value may be a function, in which case it is called with the
hash and the accessed key and the resulting value is returned.
senators.get('foo'); // -> null senators.setDefault(hancock); senators.get('foo') // -> #<Senator name="John Hancock"> senators.setDefault(function(hash, key) { return new Senator('Senator for ' + key.code); }); senators.get(VA) // -> #<Senator name="Senator for VA">
getDefault(key)
Returns the default value for the hash, or null
if none is set. The key
is
only used if the default value is a function (see setDefault()
).
equals(other)
Returns true
iff other
is a hash containing the same data (using
equality semantics) as the receiver.
fetch(key, defaultValue)
This is similar to get(key)
, but allows you to override the default value of
the hash using defaultValue
. If defaultValue
is a function it is called
with only the key as an argument. The the key is not found and no
defaultValue
is given, an error is thrown.
// Assume no default value senators.fetch(CA) // -> #<Senator name="Dianne Feinstein"> senators.fetch('foo') // -> Error: key not found senators.fetch('foo', hancock) // -> #<Senator name="John Hancock"> senators.fetch('foo', function(key) { return new Senator(key.toUpperCase()); }); // -> #<Senator name="FOO">
forEachKey(block, context)
Iterates over the keys in the hash, yielding the key each time. The optional
parameter context
sets the binding of this
within the block.
senators.forEachKey(function(key) { // key is a State });
forEachPair(block, context)
Iterates over the pairs in the hash, yielding the key and value each time. The
optional parameter context
sets the binding of this
within the block.
senators.forEachPair(function(key, value) { // key is a State, value is a Senator });
forEachValue(block, context)
Iterates over the values in the hash, yielding the value each time. The
optional parameter context
sets the binding of this
within the block.
senators.forEachValue(function(value) { // value is a Senator });
get(key)
Returns the value corresponding to the given key
. If the key is not found,
the default value for the key is returned (see setDefault()
). If no default
value exists, null
is returned.
hasKey(key)
Returns true
iff the hash contains the given key
. Aliased as includes()
.
hasValue(value)
Returns true
iff the hash contains the given value
.
includes(key)
Alias for kasKey()
.
index(value)
Alias for key()
.
invert()
Returns a new hash created by using the hash’s values as keys, and the keys as values.
isEmpty()
Returns true
iff the hash contains no data.
keepIf(predicate, context)
Deletes all the pairs that do not satisfy the predicate
from the hash.
context
sets the binding of this
within the predicate function. For
example:
// Remove pairs for California and Illinois senators.keepIf(function(pair) { return pair.key.code < 'M' }); // senators is now: // { CA => feinstein, IL => burris }
key(value)
Returns the first key from the hash whose corresponding value is value
.
Aliased as index()
.
senators.key(gillibrand); // -> #<State code="NY" name="New York">
keys()
Returns an array containing all the keys from the hash.
merge(other, block, context)
Returns a new hash containing all the key-value pairs from both the receiver
and other
. If a key exists in both hashes, the optional block
parameter is
used to pick which value to keep. If no block is given, values from other
overwrite values from the receiver. See Hash#update()
for more information.
put(key, value)
Alias for store()
.
rehash()
Call this if the state of any key changes such that its hashcode changes. This reindexes the hash and makes sure all pairs are in the correct buckets.
remove(key, block)
Deletes the given key from the hash and returns the corresponding value. If
the key is not found, the default value (see setDefault()
) is returned. If
the key is not found and the optional function block
is passed, the result
of calling block
with the key is returned.
var h = new Hash([ 'a',100, 'b',200 ]); h.remove('a') // -> 100 h.remove('z') // -> null h.remove('z', function(el) { return el + ' not found' }) // -> "z not found"
removeIf(predicate, context)
Deletes all the pairs that satisfy the predicate
from the hash. context
sets the binding of this
within the predicate function. For example:
// Remove pairs for California and Illinois senators.removeIf(function(pair) { return pair.key.code < 'M' }); // senators is now: // { NY => gillibrand, TX => hutchinson }
replace(other)
Removes all existing key-value pairs from the receiver and replaces them with
the contents of the hash other
.
shift()
Removes a single key-value pair from the hash and returns it, or returns the hash’s default value if it is already empty.
var h = new Hash(50); h.store('a', 100); h.store('b', 200); h.shift() // -> #<Pair key="a" value=100> h.shift() // -> #<Pair key="b" value=200> h.shift() // -> 50
store(key, value)
Associates the given key
with the given value
in the receiving hash. If
the state of key
changes causing a change to its hashcode, call rehash()
on the hash to reindex it. Aliased as put()
.
update(other, block, context)
Modifies the hash using the key-value pairs from the other
hash, overwriting
pairs with duplicate keys with the values from other
. If the optional
block
is passed, it can be used to decide which value to keep for duplicate
keys. The optional context
parameter sets the binding of this
within
block
.
var h = new Hash([ 'a',1, 'b',2, 'c',3 ]), g = new Hash([ 'a',5, 'b',0 ]); h.update(g, function(key, oldVal, newVal) { return oldVal > newVal ? oldVal : newVal; }); // h is now { 'a' => 5, 'b' => 2, 'c' => 3 }
values()
Returns an array containing all the values from the hash.
valuesAt(key1 [, key2 ...])
Returns an array of values corresponding to the given list of keys.