Object property-names in JavaScript must be strings. This may seem obvious, but it has serious repercussions if you’re used to the free-wheeling style of Ruby’s Hash.

a = {};
b = {};
a[b] = null;
console.log(typeof(b));
for (var i in a) {
  console.log(typeof(i));
}

In order to have the flexibility I’m used to, I whipped up this simple Hash that stores both keys and values in separate arrays.

/* JavaScript Associative Array */
function Hash(obj) {
  this.initialize(obj);
}
 
Hash.prototype = {
  initialize: function(obj) {
    this._keys   = [];
    this._values = [];
    if (obj) for (var k in obj) {
      this.set(k, obj[k]);
    }
  },
  set: function(key, value) {
    var i = this._indexOf(key);
    if (undefined == i) {
      this._keys.push(key);
      this._values.push(value);
    } else {
      this._values[i] = value;
    }
    return value;
  },
  get: function(key) {
    return this._values[this._indexOf(key)];
  },
  remove: function(key) {
    var i = this._indexOf(key);
    if (undefined == i) return;
    var s = this._keys.length - 1;
    this._keys[i]   = this._keys[s];
    this._values[i] = this._values[s];
    this._keys.pop();
    this._values.pop();
  },
  hasKey: function(key) {
    return (undefined != this._indexOf(key));
  },
  keys: function() { return this._keys.slice(0, this._keys.length); },
  values: function() { return this._values.slice(0, this._values.length); },
  eachPair: function(func) {
    for (var i = this._keys.length - 1; i >= 0; i--) {
      func(this._keys[i], this._values[i]);
    }
  },
 
  _indexOf: function(key) {
    for (var i = this._keys.length - 1; i >= 0; i--) {
      if (this._isElementSame(key, this._keys[i]))
        return i;
    }
    return undefined;
  },
  _isElementSame: function(ele1, ele2) {
    if (ele1 == ele2) return true;
    if ('string' == typeof(ele1)) return false;
    if (ele1.hasOwnProperty('length')) {
      for (var i = ele1.length - 1; i >= 0; i--) {
        if (!this._isElementSame(ele1[i], ele2[i]))
          return false;
      }
    }
    // TODO: Need to do something smarter for objects
    return true;
  }
}

Leave a Comment

Enclose code in <code lang="ruby"></code> if you care.
Preview your comment using the button below.