Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hashes of Hashes Idiom in Ruby?

Creating hashes of hashes in Ruby allows for convenient two (or more) dimensional lookups. However, when inserting one must always check if the first index already exists in the hash. For example:

h = Hash.new h['x'] = Hash.new if not h.key?('x') h['x']['y'] = value_to_insert 

It would be preferable to do the following where the new Hash is created automatically:

h = Hash.new h['x']['y'] = value_to_insert 

Similarly, when looking up a value where the first index doesn't already exist, it would be preferable if nil is returned rather than receiving an undefined method for '[]' error.

looked_up_value = h['w']['z'] 

One could create a Hash wrapper class that has this behavior, but is there an existing a Ruby idiom for accomplishing this task?

like image 357
David Avatar asked Oct 04 '08 12:10

David


People also ask

What are hashes in Ruby?

A Ruby hash is a collection of unique keys and their values. They are similar to arrays but array use integer as an index and hash use any object type. They are also called associative arrays, dictionaries or maps. If a hash is accessed with a key that does not exist, the method will return nil.

How do you make hash hash in Ruby?

Ruby hash creation. A hash can be created in two basic ways: with the new keyword or with the hash literal. The first script creates a hash and adds two key-value pairs into the hash object. A hash object is created.

How do you add two hashes in Ruby?

We can merge two hashes using the merge() method. When using the merge() method: Each new entry is added to the end. Each duplicate-key entry's value overwrites the previous value.

What is Ruby hash object?

A Hash is a dictionary-like collection of unique keys and their values. Also called associative arrays, they are similar to Arrays, but where an Array uses integers as its index, a Hash allows you to use any object type. Hashes enumerate their values in the order that the corresponding keys were inserted.


2 Answers

You can pass the Hash.new function a block that is executed to yield a default value in case the queried value doesn't exist yet:

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

Of course, this can be done recursively. There's an article explaining the details.

For the sake of completeness, here's the solution from the article for arbitrary depth hashes:

hash = Hash.new(&(p = lambda{|h, k| h[k] = Hash.new(&p)})) 

The person to originally come up with this solution is Kent Sibilev.

like image 96
Konrad Rudolph Avatar answered Oct 12 '22 14:10

Konrad Rudolph


Autovivification, as it's called, is both a blessing and a curse. The trouble can be that if you "look" at a value before it's defined, you're stuck with this empty hash in the slot and you would need to prune it off later.

If you don't mind a bit of anarchy, you can always just jam in or-equals style declarations which will allow you to construct the expected structure as you query it:

((h ||= { })['w'] ||= { })['z'] 
like image 45
tadman Avatar answered Oct 12 '22 14:10

tadman