As made clear in update 3 on this answer, this notation:
var hash = {};
hash[X]
does not actually hash the object X
; it actually
Try my JavaScript hash table implementation: http://www.timdown.co.uk/jshashtable
It looks for a hashCode() method of key objects, or you can supply a hashing function when creating a Hashtable object.
You'd have to store couplets of object/value pairs in some internal state:
HashMap = function(){
this._dict = [];
}
HashMap.prototype._get = function(key){
for(var i=0, couplet; couplet = this._dict[i]; i++){
if(couplet[0] === key){
return couplet;
}
}
}
HashMap.prototype.put = function(key, value){
var couplet = this._get(key);
if(couplet){
couplet[1] = value;
}else{
this._dict.push([key, value]);
}
return this; // for chaining
}
HashMap.prototype.get = function(key){
var couplet = this._get(key);
if(couplet){
return couplet[1];
}
}
And use it as such:
var color = {}; // Unique object instance
var shape = {}; // Unique object instance
var map = new HashMap();
map.put(color, "blue");
map.put(shape, "round");
console.log("Item is", map.get(color), "and", map.get(shape));
Of course, this implementation is also somewhere along the lines of O(n). Eugene's examples are the only way to get a hash that works with any sort of speed you'd expect from a real hash.
Another approach, along the lines of Eugene's answer is to somehow attach a unique ID to all objects. One of my favorite approaches is to take one of the built-in methods inherited from the Object superclass, replace it with a custom function passthrough and attach properties to that function object. If you were to rewrite my HashMap method to do this, it would look like:
HashMap = function(){
this._dict = {};
}
HashMap.prototype._shared = {id: 1};
HashMap.prototype.put = function put(key, value){
if(typeof key == "object"){
if(!key.hasOwnProperty._id){
key.hasOwnProperty = function(key){
return Object.prototype.hasOwnProperty.call(this, key);
}
key.hasOwnProperty._id = this._shared.id++;
}
this._dict[key.hasOwnProperty._id] = value;
}else{
this._dict[key] = value;
}
return this; // for chaining
}
HashMap.prototype.get = function get(key){
if(typeof key == "object"){
return this._dict[key.hasOwnProperty._id];
}
return this._dict[key];
}
This version appears to be only slightly faster, but in theory it will be significantly faster for large data sets.
This looks like a pretty robust solution: https://github.com/flesler/hashmap
It will even work well for functions and objects that look identical. The only hack it uses is adding an obscure member to an object to identify it. If your program doesn't overwrite that obscure variable (it's something like hashid), you're golden.
Unfortunately, none of the previous answers were good for my case: different key objects may have the same hash code. Therefore, I wrote a simple Java-like HashMap version:
function HashMap() {
this.buckets = {};
}
HashMap.prototype.put = function(key, value) {
var hashCode = key.hashCode();
var bucket = this.buckets[hashCode];
if (!bucket) {
bucket = new Array();
this.buckets[hashCode] = bucket;
}
for (var i = 0; i < bucket.length; ++i) {
if (bucket[i].key.equals(key)) {
bucket[i].value = value;
return;
}
}
bucket.push({ key: key, value: value });
}
HashMap.prototype.get = function(key) {
var hashCode = key.hashCode();
var bucket = this.buckets[hashCode];
if (!bucket) {
return null;
}
for (var i = 0; i < bucket.length; ++i) {
if (bucket[i].key.equals(key)) {
return bucket[i].value;
}
}
}
HashMap.prototype.keys = function() {
var keys = new Array();
for (var hashKey in this.buckets) {
var bucket = this.buckets[hashKey];
for (var i = 0; i < bucket.length; ++i) {
keys.push(bucket[i].key);
}
}
return keys;
}
HashMap.prototype.values = function() {
var values = new Array();
for (var hashKey in this.buckets) {
var bucket = this.buckets[hashKey];
for (var i = 0; i < bucket.length; ++i) {
values.push(bucket[i].value);
}
}
return values;
}
Note: key objects must "implement" the hashCode() and equals() methods.
JavaScript does not have a built-in map/hashmap. It should be called an associative array.
hash["X"]
is equal to hash.X
, but it allows "X" as a string variable.
In other words, hash[x]
is functionally equal to eval("hash."+x.toString())
.
It is more similar to object.properties rather than key-value mapping. If you are looking for a better key/value mapping in JavaScript, please use the Map object.