Can Ruby Do This?

In Perl, you can create a hash of arbitrary depth like so:

$hash->{foo}->{bar}->{baz} = 123;

I've tried this in ruby:

hash[:foo][:bar][:baz] = 123

but I get nil.= errors

I could of course do a check for null at each level, but oy, that's so
cumbersome. Is there a ruby way?

Thanks!

···

--
Posted via http://www.ruby-forum.com/.

Interesting little problem.

Just as a quick belt out, you could do something like this:

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

=> nil

a = infinite_hash

=> {}

a[:foo][:bar][:baz] = "barry"

=> "barry"

a

=> {:foo=>{:bar=>{:baz=>"barry"}}}

HTH
Daniel

···

On Fri, Feb 5, 2010 at 9:58 AM, Mr Bubb <jcabraham@gmail.com> wrote:

In Perl, you can create a hash of arbitrary depth like so:

$hash->{foo}->{bar}->{baz} = 123;

I've tried this in ruby:

hash[:foo][:bar][:baz] = 123

but I get nil.= errors

I could of course do a check for null at each level, but oy, that's so
cumbersome. Is there a ruby way?

Thanks!
--
Posted via http://www.ruby-forum.com/\.

$ irb
irb(main):001:0> hash[:foo][:bar][:baz] = 123
NoMethodError: undefined method `' for nil:NilClass
        from (irb):1
        from /usr/bin/irb:12:in `<main>'
irb(main):002:0> hash = { :foo => { :bar => { :baz => 123} } }
=> {:foo=>{:bar=>{:baz=>123}}}
irb(main):003:0> hash[:foo][:bar][:baz]
=> 123

I'm not sure if there's a way that's a little less verbose...

···

On Thu, Feb 4, 2010 at 10:58 PM, Mr Bubb <jcabraham@gmail.com> wrote:

In Perl, you can create a hash of arbitrary depth like so:

$hash->{foo}->{bar}->{baz} = 123;

I've tried this in ruby:

hash[:foo][:bar][:baz] = 123

but I get nil.= errors

I could of course do a check for null at each level, but oy, that's so
cumbersome. Is there a ruby way?

Thanks!
--
Posted via http://www.ruby-forum.com/\.

In Perl, you can create a hash of arbitrary depth like so:

$hash->{foo}->{bar}->{baz} = 123;

It is a bit tricky because you have to specify a default
proc that refers to itself (to get the infinite depth):

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

=> #<Proc:0x01224e20@(irb):3>

top = Hash.new(&default)

=> {}

?> top[0][1][2][3] = 4
=> 4

?> p top
{0=>{1=>{2=>{3=>4}}}}
=> nil

Gary Wright

···

On Feb 4, 2010, at 5:58 PM, Mr Bubb wrote:

Yes.

You need to understand Ruby a little better, though.

h = {}
p h[:a]

=> nil

That is, if you have a hash, and you reference an element in the hash
that does not exist, the default behavior is for the hash to return
nil.

So, if you do this:

h = {}
p h[:a][:b]

You will get this:

NoMethodError: undefined method `' for nil:NilClass

This is because h[:a] returns a nil, as you already discovered, and
NilClass does not define an method. Since, in Ruby, you are just
chaining method calls, your h[:a][:b][:c] style syntax can only work
if the return element from any given level is an object that provides
an method.

Now, consider this example:

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

=> {}

Ah! Now we're on to something.

h = Hash.new {|h,k| h[k] = {}}
p h[:a][:b]

=> nil

Hmmm.

h = Hash.new {|h,k| h[k] = {}}
p h[:a][:b][:c]

=> NoMethodError: undefined method `' for nil:NilClass

Oh, dang. Do you see what's happening here? Hash's new method can
take a block. If it is given a block, then it will call that block
with arguments of the hash, itself, and the key that was given, if
it's asked to lookup a key that does not exist in the hash. So, h[:a]
ran that block, creating a value at :a => {}. h[:a][:b], though, gets
us back to the default Hash behavior. Still, you see one solution
here, I bet:

h = Hash.new {|h,k| h[k] = Hash.new {|h2,k2| h2[k2] = {}}}
h[:a][:b][:c] = 123
p h[:a][:b][:c]

=> 123

That works. It's kind of hard to easily read, though. So, you could
rewrite it like this:

h = Hash.new do |h1,k1|
  h1[k1] = Hash.new do |h2,k2|
    h2[k2] = {}
  end
end

That, at least, lets the eye follow along a little better, but it's
still limited because you must decide what depth of auto
initialization you are going to support ahead of time. If you try
something like h[:a][:b][:c][:d][:e] you will again get that
unpleasant NoMethodError.

However, step back just a moment. The hash initializer accepts a
block. And, if you look at the Hash methods, you'll see a
Hash#default_proc.

It does just what it sounds like it does -- it returns the default
proc, which is what the block is turned into when the new method
captures it and stores it.

So, all you need to do is this:

h = Hash.new {|hash,key| hash[key] = Hash.new(&hash.default_proc)}
h[:a][:b][:c][:d][:e] = 12345
p h[:a][:b][:c][:d][:e]

=> 12345

Hopefully this helps you follow what's happening with Ruby's method
calls and Hash initializers.

Kirk Haines

···

On Thu, Feb 4, 2010 at 3:58 PM, Mr Bubb <jcabraham@gmail.com> wrote:

In Perl, you can create a hash of arbitrary depth like so:

$hash->{foo}->{bar}->{baz} = 123;

I've tried this in ruby:

hash[:foo][:bar][:baz] = 123

but I get nil.= errors

I could of course do a check for null at each level, but oy, that's so
cumbersome. Is there a ruby way?

Mr Bubb wrote:

In Perl, you can create a hash of arbitrary depth like so:

$hash->{foo}->{bar}->{baz} = 123;

I've tried this in ruby:

hash[:foo][:bar][:baz] = 123

but I get nil.= errors

I could of course do a check for null at each level, but oy, that's so
cumbersome. Is there a ruby way?

Sure, but there's probably also a better way that doesn't involve so
much Hash nesting. Can you explain what you're using this for?

Thanks!

Best,

···

--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
--
Posted via http://www.ruby-forum.com/\.

Well, in my job (bioinformatics), I only use code like this about a
hundred times a day. I've been doing this job for 15 years, and
actually, there is no better way. You must not need to quickly create
data structures from flat files, I guess.

Jim

Marnen Laibow-Koser wrote:

···

Sure, but there's probably also a better way that doesn't involve so
much Hash nesting. Can you explain what you're using this for?

--
Posted via http://www.ruby-forum.com/\.

Mr Bubb wrote:

Well, in my job (bioinformatics), I only use code like this about a
hundred times a day. I've been doing this job for 15 years, and
actually, there is no better way.

If you're so sure of that, why are you asking for help? There may be no
better way in Perl, but Ruby is not Perl. :slight_smile:

You must not need to quickly create
data structures from flat files, I guess.

Instead of assuming what my needs are, let's focus on yours. Nested
hashes *may* be the best solution in Ruby, but if you provide more
information about your use case, we can give you better help.

Jim

Best,

···

--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
--
Posted via http://www.ruby-forum.com/\.

Let me know if my detailed explanation didn't make sense. The long
and the short of it is this:

auto_nesting_hash = Hash.new {|hash,key| hash[key] =
Hash.new(&hash.default_proc)}

It may well be the best way to do whatever it is you are doing. What,
I think, Marnen was trying to communicate, though, is that there may
be an alternative that performs better in Ruby which would become more
obvious if we understood the actual use a little better.

Just as a quick example, there was a thread last week where someone
was asking about while loop performance, and wondering why a while
loop in Ruby isn't so fast as he expected.

i = 0
while i < 10000000
  i += 1
end

Perfectly legitimate Ruby, and comparable to similar looping
structures in other languages. But, the real answer there is that
there's another way to do it in Ruby that's substantially faster.

0.upto(10000000) {|i| #do whatever you need to do with i }

So, if the auto nesting hashes work for you, that's great. If you
want to bend people's minds towards perhaps suggesting a better
alternative, though, provide more details. Someone may surprise you
with something unexpected, and superior.

Kirk Haines

···

On Thu, Feb 4, 2010 at 7:33 PM, Mr Bubb <jcabraham@gmail.com> wrote:

Well, in my job (bioinformatics), I only use code like this about a
hundred times a day. I've been doing this job for 15 years, and
actually, there is no better way. You must not need to quickly create
data structures from flat files, I guess.

Marnen Laibow-Koser wrote:

If you're so sure of that, why are you asking for help? There may be no
better way in Perl, but Ruby is not Perl. :slight_smile:

Is there a hidden camera, or something? It's not legit to say "I'm a
ruby newbie, is there a way to do x in ruby?" If I had said, hey perl
has one line comments, how do you do one line comments in ruby, would
you answer, well, why do you need one line comments? Jeez.

You must not need to quickly create
data structures from flat files, I guess.

Instead of assuming what my needs are, let's focus on yours. Nested
hashes *may* be the best solution in Ruby, but if you provide more
information about your use case, we can give you better help.

This is like the old Bostonian joke:

Question: "Can you tell me how to get to Roxbury?"
Answer: "Whaddaya wanna go there for?"

I guess what I'm saying is, thanks anyway.

···

--
Posted via http://www.ruby-forum.com/\.

Well, in my job (bioinformatics), I only use code like this about a
hundred times a day. I've been doing this job for 15 years, and
actually, there is no better way. You must not need to quickly create
data structures from flat files, I guess.

Let me know if my detailed explanation didn't make sense. The long
and the short of it is this:

auto_nesting_hash = Hash.new {|hash,key| hash[key] =
Hash.new(&hash.default_proc)}

IMHO this is the most concise and elegant way to do it in Ruby! I am surprised you are the only advocate of this.

Just as a quick example, there was a thread last week where someone
was asking about while loop performance, and wondering why a while
loop in Ruby isn't so fast as he expected.

i = 0
while i< 10000000
   i += 1
end

Perfectly legitimate Ruby, and comparable to similar looping
structures in other languages. But, the real answer there is that
there's another way to do it in Ruby that's substantially faster.

0.upto(10000000) {|i| #do whatever you need to do with i }

This one does one iteration more than the original code if I'm not mistaken. There's also

10000000.times {|i| #do whatever you need to do with i }

So, if the auto nesting hashes work for you, that's great. If you
want to bend people's minds towards perhaps suggesting a better
alternative, though, provide more details. Someone may surprise you
with something unexpected, and superior.

Just as a vague idea: a similar thing could be created with OpenStruct accesses - saves you a lot of typing of . Then you could do hash.foo.bar.baz = 123

class AutoNest
   def method_missing(s,*a,&b)
     case s
     when /\A(\w+)=\z/
       class <<self;self;end.class_eval { attr_accessor $1 }
       send(s,*a)
     when /\A\w+\z/
       class <<self;self;end.class_eval { attr_accessor s }
       x = self.class.new
       send("#{s}=",x)
       x
     else
       super
     end
   end
end

hash = AutoNest.new
hash.foo.bar.baz = 123
p hash, hash.foo.bar.baz

I'd probably stick with your solution though.

Kind regards

  robert

···

On 05.02.2010 06:01, Kirk Haines wrote:

On Thu, Feb 4, 2010 at 7:33 PM, Mr Bubb<jcabraham@gmail.com> wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

No I wouldn't, if on the other hand you came to me and asked "How do you do write a 'for' loop in Ruby?"
I would indeed ask "why do you need a 'for' loop?" Why ask? Because while writing something akin to a 'for'
is *possible* in Ruby, Ruby has other methods of achieving the same result which are preferred. Thus,
seeing as your original question has been dealt with by others, it makes perfect sense to ask _why_
you would want to do something that just doesn't seem like the best _Ruby_ solution in most cases.

···

On 2/4/2010 7:46 PM, Mr Bubb wrote:

Marnen Laibow-Koser wrote:
   

If you're so sure of that, why are you asking for help? There may be no
better way in Perl, but Ruby is not Perl. :slight_smile:
     

Is there a hidden camera, or something? It's not legit to say "I'm a
ruby newbie, is there a way to do x in ruby?" If I had said, hey perl
has one line comments, how do you do one line comments in ruby, would
you answer, well, why do you need one line comments? Jeez.

Robert Klemme:

But, the real answer there is that there's another
way to do it in Ruby that's substantially faster.

0.upto(10000000) {|i| #do whatever you need to do with i }

This one does one iteration more than the original
code if I'm not mistaken. There's also

10000000.times {|i| #do whatever you need to do with i }

A (very) minor nitpick, but – as this thread will hopefully turn
into a newbie magnet – worth noting that the above are not perfectly
equivalent; Integer#upto will go all the way up to 10000000, while
Integer#times will iterate 10000000 times (so actually one less). :slight_smile:

Just as a vague idea: a similar thing could be created with OpenStruct
accesses - saves you a lot of typing of . Then you could do
hash.foo.bar.baz = 123

If the nesting represents any kind of object structure (which in this
case it seems to do), I’d actually prefer the OpenStruct solution.

— Shot

···

On 05.02.2010 06:01, Kirk Haines wrote:

--
There are two hard problems in computer science: cache
invalidation, naming things, and off-by-one errors.

Walton Hoops wrote:

···

On 2/4/2010 7:46 PM, Mr Bubb wrote:

Marnen Laibow-Koser wrote:
   

If you're so sure of that, why are you asking for help? There may be no
better way in Perl, but Ruby is not Perl. :slight_smile:
     

Is there a hidden camera, or something? It's not legit to say "I'm a
ruby newbie, is there a way to do x in ruby?" If I had said, hey perl
has one line comments, how do you do one line comments in ruby, would
you answer, well, why do you need one line comments? Jeez.
   

No I wouldn't, if on the other hand you came to me and asked "How do you
do write a 'for' loop in Ruby?"
I would indeed ask "why do you need a 'for' loop?" Why ask? Because
while writing something akin to a 'for'
is *possible* in Ruby, Ruby has other methods of achieving the same
result which are preferred. Thus,
seeing as your original question has been dealt with by others, it makes
perfect sense to ask _why_
you would want to do something that just doesn't seem like the best
_Ruby_ solution in most cases.

Well said. If you want to get the most out of a language, it's worth
investigating its idioms. Sometimes they are not worth copying, but
more often they are. You can write Fortran in Ruby, or Lisp in C++, but
usually you don't want to.

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
--
Posted via http://www.ruby-forum.com/\.