1

In other languages it is possible to create a generic 2D hash. I know creating 2d hashes is possible in javascript as well as explained here, but I can't seem to find a generic way to achieve this.

As an example of what I am looking for. In Ruby you can do this:

2dhash = Hash.new{|h, k| h[k] = Hash.new }

puts 2dhash["test"]["yes"]
#=> nil
2dhash[1][2] = "hello"
puts 2dhash[1][2]
#=> "hello"

Notice that I have not initialized the second level of hash, it happens automatically.

Is it possible to somehow achieve the same in javascript? Specifically, a way to make a 2d hash without initializing the first level of hash (or hard-coding it to be even more specific). The 2dhash will be used dynamically, so I have no clue what the first level will be.

Community
  • 1
  • 1
Automatico
  • 12,420
  • 9
  • 82
  • 110

2 Answers2

1

Looks like a nice data structure excercise, let me try :D

function Hash() {
  this.hash = {};
}

Hash.prototype.set = function(val) {
  var paths = Array.prototype.slice.call(arguments, 1) // all levels
  var path = paths.shift() // first level
  var hashed = this.hash[path]

  if (paths.length) {
    // still have deeper levels
    if (!(hashed instanceof Hash)) {
      hashed = this.hash[path] = new Hash()
    }
    Hash.prototype.set.apply(hashed, [val].concat(paths))
  } else {
    // last level
    this.hash[path] = val
  }
}

Hash.prototype.get = function() {
  var paths = Array.prototype.slice.call(arguments, 0) // all levels
  var path = paths.shift() // first level
  var hashed = this.hash[path]

  if (paths.length) {
    // still have deeper levels
    return Hash.prototype.get.apply(hashed, paths)
  } else {
    // last level
    return hashed
  }
}

Now, let's see if it works:

var trytry = new Hash()
trytry.set('the value to store', 'key1', 'key2')

trytry.get('key1')         // Hash{key2: 'the value to store'}
trytry.get('key1', 'key2') // 'the value to store'

Hooray it works!

It also works for even deeper levels:

trytry.set('the value to store', 'key1', 'key2','key3', 'key4')
trytry.get('key1', 'key2','key3') // Hash{key4: 'the value to store'}

However, a disadvantage of this approach is that you have to use instance methods get and set, rather than native object literal getter/setter.

It's still incomplete. For production environment, we need to do more, e.g. methods and properties like contains, size, etc.

Leo
  • 13,428
  • 5
  • 43
  • 61
  • Awesome @Leo! Much nicer than what I was suggesting by using `__noSuchMethod__`. – Automatico Apr 28 '15 at 10:48
  • @Cort3z It's just an excercise, if you'd like to use in a production environment, you might need to improve it with methods and properties like `contains`, `size`, etc. :D – Leo Apr 28 '15 at 11:21
  • Yes, indeed. I am also looking at possibly having a bit different behaviour when you are accessing a key which does not exist. I think it would be nice if it returned an empty hash if it is a level1 value, and null/undefined if it is a level2+. – Automatico Apr 28 '15 at 11:47
0

If you initialize the first level of the hash with objects, then you can reference the second level without typeErrors, even if the data was not defined before.

Example:

var _2dhash = {a: {}, b: {}, c:{}} 
//Note you cannot start variable names with numbers in js
_2dhash['a']['missingElement'];
// > undefined

It works because you're accessing undefined properties of defined objects. If you try to access through a missing top-level object, ie.

_2dhash['d']['whatever'];

You will get a TypeError, because _2dhash.d was not defined, and the second lookup fails, trying to read the 'whatever' property of undefined.

doldt
  • 4,466
  • 3
  • 21
  • 36
  • Maybe I was a bit unclear, I will update the question, but I am specifically looking for a way which does not require me to initialize the first level of hash. It will be used dynamially, so there is no way for me to know what that level will be. – Automatico Apr 28 '15 at 08:43
  • @Cort3z I'm afraid you can't do that - see my edited elaboration for the reasons. – doldt Apr 28 '15 at 08:44
  • So maybe it is possible to make if we hook into the `__noSuchMethod__` property of the object. Humm.. I will investigate. – Automatico Apr 28 '15 at 08:49
  • @Cort3z You'll run into problems down that path. First of all that API is obsolete/nonstandard, and the failed lookup is on the undefined object, not your hash object. – doldt Apr 28 '15 at 08:54