Hash of hash?

hi,

I am totally new to Ruby and am trying to code a hash of a hash.
Somehow I always get a syntax error, but am unable to resolve it.

This is what I have:

File 1:
class Hashhash
def initialize
@dt = { 2 => {2 => “H”, 3 => “C”},
3 => {2 => “H”, 9 => “Dh”},
“T” => {2 => “C”, 9 => “S”}
}

 return @dt

end
end

File 2:
require ‘Hashhash’

ah = Hashhash.new

out = ah[“T”][9]

p out

I always get this error:
test.rb:5: undefined method `[]’ for #Hashhash:0x2bd2e30
(NoMethodError)

But when I try to code something in irb it works:
irb(main):006:0* dt = { 2 => {2 => “H”, 3 => “C”}}
=> {2=>{2=>“H”, 3=>“C”}}
irb(main):007:0> puts dt[2][2]
H
=> nil

What am I doing wrong??

Thanks in advance

Jeroen L. [email protected] wrote:

class Hashhash

end

out = ah[“T”][9]

test.rb:5: undefined method `[]’ for #Hashhash:0x2bd2e30

Hi Jeroen,

I think you have to explicitly define a [] method for your class
(and a []= method, if you want to write values with this syntax too).

Flo

On Friday 19 June 2009, Jeroen L. wrote:

| @dt = { 2 => {2 => “H”, 3 => “C”},
|
|But when I try to code something in irb it works:
|irb(main):006:0* dt = { 2 => {2 => “H”, 3 => “C”}}
|=> {2=>{2=>“H”, 3=>“C”}}
|irb(main):007:0> puts dt[2][2]
|H
|=> nil
|
|What am I doing wrong??
|
|Thanks in advance

First of all, what you get is not a syntax error (which would have
produced an
error message like: syntax error, …). The error you see is caused by
calling a method on an object for which it isn’t defined.

The problem is that when you call the new method of a class (in your
case,
Hashhash.new), you don’t get the value returned by initialize, but the
object
created by new. This means that ah doesn’t contain a hash, but a
Hashhash,
which doesn’t have a [] method.

You have several options on how to do what you want. Depending on what
exactly
you need, I think the easiest ones are:

  • define an [] method for the Hashhash class. This way, the code you
    wrote
    will work exactly as you meant. Defining such a method is easy. Inside
    the
    definition of the Hashhash class, do:

    def
    @dt[key]
    end

Calling this method will return the hash stored in @dt under key. Since
this
is a hash, you’ll be able to retrieve the value corresponding to the key
2

  • define a method for the Hashhash class which returns the instance
    variable
    @dt and use that to access the elements. To define a method which only
    returns
    the value of an instance variable, you can use the attr_reader class
    method:

    class Hashhash

    attr_reader :dt

    def initialize

    end

    end

Then, you can write

ha = Hashhash.new

ha.dt[2][2]

  • Do not use the Hashhash class and use a simple hash of hashes:

    ah = { 2 => {2 => “H”, 3 => “C”},
    3 => {2 => “H”, 9 => “Dh”},
    “T” => {2 => “C”, 9 => “S”}
    }
    ah[2][2]

As for the reason it works in irb… well, from what you show us, you
aren’t
using the same code you used in the script. At least, in this case dt
isn’t an
instance variable (since it doesn’t start with @).

I hope this helps

Stefano

2009/6/19 Florian W. [email protected]:

I think you have to explicitly define a [] method for your class
(and a []= method, if you want to write values with this syntax too).

Alternatively Jeroen can simply create a Hash with a default_proc
which will recursively insert Hashes:

irb(main):001:0> ha = Hash.new {|h,k| h[k] = Hash.new(&h.default_proc)}
=> {}
irb(main):002:0> ha[1][2][3]
=> {}
irb(main):003:0> ha[1][2][3]=123
=> 123
irb(main):004:0> ha
=> {1=>{2=>{3=>123}}}
irb(main):005:0>

Kind regards

robert

Hi,

Am Freitag, 19. Jun 2009, 23:08:12 +0900 schrieb Jeroen L.:

class Hashhash
def initialize
@dt = { 2 => {2 => “H”, 3 => “C”},
3 => {2 => “H”, 9 => “Dh”},
“T” => {2 => “C”, 9 => “S”}
}
return @dt
end
end

def [] key ; @dt[ key] ; end

You don’t need to return anything from initialize' because its return value is discarded and the invokingnew’ method always
returns the created object. Btw you don’t need to return anything
rom assigment methods `member=’ either.

Bertram

Thanks all for the replies, very clear.

I think I will implement an accessor to the hash, I come from a Perl
world and would build the hash in a seperate routine (to hide the
initialisation) that returns a reference to the hash.

Would it be possible to make Hashhash inherit from Hash (by using <
Hash) and be able to use the [][] operator immediately in a way?

On 19.06.2009 17:42, Jeroen L. wrote:

Thanks all for the replies, very clear.

I think I will implement an accessor to the hash, I come from a Perl
world and would build the hash in a seperate routine (to hide the
initialisation) that returns a reference to the hash.

If you create a new class then the constructor is just sufficient - you
do not need another method that hides away invocation of YourClass.new.

Would it be possible to make Hashhash inherit from Hash (by using <
Hash) and be able to use the [][] operator immediately in a way?

Actually, you do not need to create another class (see my earlier
example). For that it might make sense to create a method that returns
a new instance with the initialization I have shown.

If you lack explanation: the construct Hash.new {|h,k| …} creates a
new Hash instance with its default_proc set to the block you provide.
This block is invoked whenever there is a key miss. In my case I just
built it in a way as to create another Hash on the fly with the same
default_proc and stuff it in the other Hash. The default_proc
propagation ensures you can arbitrarily nest Hashes. Of course, you can
also do something like this if you know you just need one level of
nested Hashes

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

Question is whether you want to bother to create a class for this. IMHO
this makes only sense if you want to put additional logic into it (e.g.
checking elements that are placed in the Hash).

Kind regards

robert