# X=\[\]; x\[:bla\]\[:some\_key\] does not work?

**URL:** <https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150>\
**Category:** Ruby\
**Created:** [November 3, 2007, 9:21pm UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150 "2007-11-03T21:21:01Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 3, 2007, 9:21pm UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/1 "2007-11-03T21:21:01Z")

</div>

Hi all

PHP lets me easily create multidimensional hashes using the following  
syntax:

x = array();  
x[“bla”][“some\_key”] = true;

Is Ruby not capable of doing this?

x = []  
x[:bla][:some\_key] = true

gives me a nil error!

What wrong here?

Thanks Josh

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 3, 2007, 9:29pm UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/2 "2007-11-03T21:29:24Z")

</div>

Hi –

On Sun, 4 Nov 2007, Joshua M. wrote:

> x = []  
> x[:bla][:some\_key] = true
> 
> gives me a nil error!

Are you sure it’s not giving you a “Symbol as array index” error?

> What wrong here?

A couple of things. You’re indexing an array with a symbol, but you  
have to use an integer. You’re also expecting a non-existent array  
element to be something other than nil, but it isn’t:

a = []  
a[1] # nil

You’re then trying (or would be, if you used an integer instead of  
:bla) to call the method [] on nil, and nil has no such method.  
(Remember that the index syntax, a[n], is actually a method call:  
a..)

David

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 3, 2007, 9:33pm UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/3 "2007-11-03T21:33:35Z")

</div>

Ups I’m sorry, I messed things up because PHP uses [] for both arrays  
and hashes.

x = array();  
x[“bla”][“some\_key”] = true;

Is Ruby not capable of doing this?

x = {}  
x[:bla][:some\_key] = true

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 3, 2007, 9:42pm UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/4 "2007-11-03T21:42:16Z")

</div>

Joshua M. wrote:

> 

For a 2-dimensional Hash you can use:

h = Hash.new({})

See the documentation of the initialize method of the Hash class. Here  
it basically tells the object to use a default value (an empty hash) for  
uninitialized keys.

Lionel

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 3, 2007, 9:44pm UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/5 "2007-11-03T21:44:01Z")

</div>

Jano S. wrote:

> Please note that it is not enough to write Hash.new { {} } as the  
> inner will create one particular Hash instance, that all keys will  
> reference. You need Hash.new to create a new hash for each key.

Ooopps, my bad.

Lionel

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 3, 2007, 9:39pm UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/6 "2007-11-03T21:39:21Z")

</div>

On 11/3/07, Joshua M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> x = []  
> x[:bla][:some\_key] = true
> 
> gives me a nil error!

As David said, if you want to create hashes, you have to create Hashes  
😉

Then, you can tell Hash.new what is the default value, so you can  
create hash, that will contain by default empty hashes:

x = Hash.new { Hash.new }

This will add an “automatic” two level hash. I don’t know quickly how  
to make this indefinitly deep, you can at least repeat the pattern.

Please note that it is not enough to write Hash.new { {} } as the  
inner will create one particular Hash instance, that all keys will  
reference. You need Hash.new to create a new hash for each key.

Jano

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 3, 2007, 9:49pm UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/7 "2007-11-03T21:49:54Z")

</div>

Lionel B. wrote:

> For a 2-dimensional Hash you can use:
> 
> h = Hash.new({})

That won’t result in the behaviour most people would expect from a  
2-dimensional hash:

> > h = Hash.new({})  
> > =\> {}
> 
> > h[:a][:foo] = “bar”  
> > =\> “bar”
> 
> > h[:a]  
> > =\> {:foo=\>“bar”}
> 
> > h  
> > =\> {}
> 
> > h[:b][:chunky] = “bacon”  
> > =\> “bacon”
> 
> > h[:b]  
> > =\> {:chunky=\>“bacon”, :foo=\>“bar”}
> 
> > h[:c]  
> > =\> {:chunky=\>“bacon”, :foo=\>“bar”}
> 
> > h  
> > =\> {}

You want to a) use the block-form of Hash.new to not use the same  
instance of  
a hash everytime and b) assign the new Hash instead of just returning  
it.

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 3, 2007, 9:51pm UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/8 "2007-11-03T21:51:40Z")

</div>

Hi –

On Sun, 4 Nov 2007, Jano S. wrote:

> > 
> 
> x = Hash.new { Hash.new }
> 
> This will add an “automatic” two level hash. I don’t know quickly how  
> to make this indefinitly deep, you can at least repeat the pattern.
> 
> Please note that it is not enough to write Hash.new { {} } as the  
> inner will create one particular Hash instance, that all keys will  
> reference. You need Hash.new to create a new hash for each key.

That’s not quite right. The only-one-object thing is when you do this:

h = Hash.new({})

If you use a block, it gets executed each time – so in your example,  
a new hash would get created.

However, it’s important to remember that what you’re setting is the  
default value (or behavior) for _non-existent_ keys. So if you do:

h = Hash.new { {} }  
a = h[1]

a is now a hash, but h still has no keys.

The block is automatically passed the hash itself, and the key:

h = Hash.new {|hash,key| # do stuff with hash and key }

and you can use that fact to actually add the key to the hash.

David

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 3, 2007, 9:47pm UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/9 "2007-11-03T21:47:33Z")

</div>

Joshua M. wrote:

> PHP lets me easily create multidimensional hashes[…]  
> Is Ruby not capable of doing this?
> 
> x = []

That’s an array. A hash would be x={}

> x[:bla][:some\_key] = true
> 
> gives me a nil error!

The code above should give you a different error. If you use {} it  
should give  
you a nil error because x[:bla] would return nil and nil doesn’t have a  
method  
[].  
To fix that you have to make x return a hash for non-existant keys. That  
would  
work as such:  
x = Hash.new {|h,k| h[k] = Hash.new}  
x[:bla][:some\_key] = true

Or if you want an arbitrary amount of nesting:

blk = lambda {|h,k| h[k] = Hash.new(&blk)}  
x = Hash.new(&blk)  
x[:la][:li][:lu][:chunky][:bacon][:foo] = “bar”

HTH,  
Sebastian

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 3, 2007, 9:53pm UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/10 "2007-11-03T21:53:42Z")

</div>

Joshua M. wrote:

> Hi all
> 
> PHP lets me easily create multidimensional hashes using the following  
> syntax:
> 
> x = array();  
> x[“bla”][“some\_key”] = true;
> 
> Is Ruby not capable of doing this?
> 
> x = []  
> x[:bla][:some\_key] = true
> 
> gives me a nil error!

x = []  
x[:blah][:some\_key] = true

–output:–  
`[]’: Symbol as array index (TypeError)  
from r5test.rb:2

In ruby, array indexes must be non-negative integers-not symbols, not  
strings, not floats…err this actually ‘works’:

x = []  
x[3.5] = true  
puts x[3.5]

–output:–  
true

But, I think that ruby must convert the float to an int. However, this  
works:

puts x.values\_at(3.5)

–output:–  
true

But, then so does this:

puts x.values\_at(3)

–output:–  
true

So, I can’t really figure out a way to prove that the index value can’t  
be a float. Anyway…

To create nested hashes you can do this:

x = {}  
x[‘a’] = 10  
p x

x[:bla] = {1=\>10, 2=\>20}  
p x

–output:–  
{“a”=\>10}  
{“a”=\>10, :bla=\>{1=\>10, 2=\>20}}

Writing this is problematic, though:

x = {}  
x[:bla][:some\_key] = true

–output:–  
undefined method `[]=’ for nil:NilClass (NoMethodError)

That is equivalent to:

x = {}  
lookup1 = x[:bla]  
lookup1[:some\_key] = true

and when you lookup a non-existent key for a hash, it returns nil. As  
a result, lookup1 is assigned nil.

But in ruby you can make hashes to return whatever you want when you  
lookup a non-existent key. You do that by specifying what you want  
returned in the Hash constructor:

x = Hash.new {|hash, key| hash[key] = {} }  
x[:bla][:some\_key] = true  
p x

–output:–  
{:bla=\>{:some\_key=\>true}}

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 3, 2007, 10:13pm UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/11 "2007-11-03T22:13:31Z")

</div>

lol. Waaaaaaaaaay too late.

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 4, 2007, 2:41am UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/12 "2007-11-04T02:41:39Z")

</div>

On Sat, 03 Nov 2007 15:44:48 -0500, sepp2k wrote:

> > gives me a nil error!
> 
> blk = lambda {|h,k| h[k] = Hash.new(&blk)} x = Hash.new(&blk)  
> x[:la][:li][:lu][:chunky][:bacon][:foo] = “bar”
> 
> HTH,  
> Sebastian

sepp2k’s answer is the only correct answer in this thread.

–Ken

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 4, 2007, 2:42am UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/13 "2007-11-04T02:42:49Z")

</div>

On Sat, 03 Nov 2007 15:44:48 -0500, sepp2k wrote:

> > gives me a nil error!
> 
> blk = lambda {|h,k| h[k] = Hash.new(&blk)} x = Hash.new(&blk)  
> x[:la][:li][:lu][:chunky][:bacon][:foo] = “bar”
> 
> HTH,  
> Sebastian

Or use Hash#autonew from the facets gem, which does precisely the same  
thing.

–Ken

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 4, 2007, 2:46am UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/14 "2007-11-04T02:46:54Z")

</div>

Hi –

On Sun, 4 Nov 2007, Ken B. wrote:

> sepp2k’s answer is the only correct answer in this thread.

Could you point out the mistakes in my answers for me? I’d be  
interested. Thanks –

David

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 4, 2007, 3:42am UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/15 "2007-11-04T03:42:26Z")

</div>

On Sat, 03 Nov 2007 20:46:00 -0500, dblack wrote:

> David  
> The Hash.new constructor has several different behaviors depending on  
> what you pass it:

Hash.new(param)  
creates a new hash where the object in param is returned whenever a  
specific key is not found. No new key is created in the hash:  
a=Hash.new(0) =\> {}  
a[:foo] =\> 0  
a =\> {}

Additionally, this same object is constructed once, and used every time  
you call the hash so:  
Foo=Struct.new(:bar) =\> Foo  
h=Hash.new(Foo.new) =\> {}  
h[:baz].bar=“Hello” =\> “Hello”  
h =\> {}  
h[:baz] =\> #  
h[:furby] =\> #

I haven’t created any keys, here, but I’ve changed the default value of  
the hash.

Hash.new{code}  
creates a new hash where default values are generated by executing the  
block {code}. When used as  
Hash.new{param}  
the code inside the block is called every time, and a new object is  
constructed every time. (because {} and [] construct a new object every  
time they are encountered in execution, here they are encountered every  
time the block is called, whereas before they were encountered only once  
when the hash was constructed). But the semantics of key creation are  
the  
same as before – no new keys are created until you explicitly assign to  
the hash.

a=Hash.new{0} =\> {}  
a[:foo] =\> 0  
a =\> {}

Foo=Struct.new(:bar) =\> Foo  
h=Hash.new{Foo.new} =\> {}  
h[:baz].bar=“Hello” =\> “Hello”  
h =\> {}  
h[:baz] =\> #  
h[:furby] =\> #

Hash.new{|h,k| h[k]=param}  
this finally assigns to the hash when you access the hash.

Foo=Struct.new(:bar) =\> Foo  
h=Hash.new{|h,k| h[k]=Foo.new} =\> {}  
h[:baz].bar=“Hello” =\> “Hello”  
h =\> {:baz=\>#}  
h[:baz] =\> #  
h[:furby] =\> #  
h =\> {:baz=\>#, :furby=\>#}

Personally, I think that Hash might benefit from having a few more named  
constructors so that people can guess the expected behavior by name.  
Particularly one that automatically does the whole  
Hash.new{|h,k| h[k]=param}  
thing, through judicious use of #dup.

class Hash  
def self.new\_add obj  
Hash.new{|h,k| h[k]=obj.dup}  
end  
end

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 3, 2007, 10:11pm UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/16 "2007-11-03T22:11:49Z")

</div>

Jano S. wrote:

> x = Hash.new { Hash.new }
> 
> This will add an “automatic” two level hash. I don’t know quickly how  
> to make this indefinitly deep, you can at least repeat the pattern.
> 
> Please note that it is not enough to write Hash.new { {} } as the  
> inner will create one particular Hash instance, that all keys will  
> reference. You need Hash.new to create a new hash for each key.
> 
> Jano

As far as I can tell neither of the following does anything:

x = Hash.new { Hash.new }  
y = Hash.new{ {} }

x[:bla][:some\_key] = true  
y[:bla][:some\_key] = true

p x, y

–output:–  
{}  
{}

From the docs for Hash.new:

```
 If a block is
 specified, it will be called with the hash object and the key, and
 should return the default value. It is the block's responsibility
 to store the value in the hash if required.

```

Your lookup of a non-existent key returns a hash, but the returned hash  
never gets stored in the original hash, so the original hash remains  
empty.

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 4, 2007, 7:19am UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/17 "2007-11-04T07:19:45Z")

</div>

David A. Black wrote:

> Hi –
> 
> On Sun, 4 Nov 2007, Ken B. wrote:
> 
> > sepp2k’s answer is the only correct answer in this thread.
> 
> Could you point out the mistakes in my answers for me? I’d be  
> interested. Thanks –

In your first post, you misspelled the op’s name:

> David A. Black wrote:  
> Hi –

and then you followed that up with the very same mistake in your second  
post:

> David A. Black wrote:  
> Hi –

Can you blame the indignation felt by subsequent readers?

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 4, 2007, 7:30am UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/18 "2007-11-04T07:30:31Z")

</div>

Ken B. wrote:

> On Sat, 03 Nov 2007 15:44:48 -0500, sepp2k wrote:
> 
> > > gives me a nil error!
> > 
> > blk = lambda {|h,k| h[k] = Hash.new(&blk)} x = Hash.new(&blk)  
> > x[:la][:li][:lu][:chunky][:bacon][:foo] = “bar”
> > 
> > HTH,  
> > Sebastian
> 
> sepp2k’s answer is the only correct answer in this thread.

Darn it! I knew someone would spot the error here:

x = Hash.new {|hash, key| hash[key] = {} }  
x[:bla][:some\_key] = true  
p x

–output:–  
{:bla=\>{:some\_key=\>true}}

But I couldn’t figure out how to do it, so I fudged the output. It was  
really:

–output:–  
Nuh uh, shorty. Don’t bring that stepped on hash round here no mo’.

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 4, 2007, 10:34am UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/19 "2007-11-04T10:34:52Z")

</div>

> x=[]; x[:bla][:some\_key] does not work?  
> Posted by Joshua M. (josh) on 03.11.2007 21:21  
> Hi all
> 
> PHP lets me easily create multidimensional hashes using the following  
> syntax:
> 
> x = array();  
> x[“bla”][“some\_key”] = true;
> 
> Is Ruby not capable of doing this?
> 
> x = []  
> x[:bla][:some\_key] = true
> 
> gives me a nil error!
> 
> What wrong here?

How about this approach:

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

a[2][1]=2  
a[2][2][3]=4  
a[3][1][1][1]=1

p a #=\> {2=\>{1=\>2, 2=\>{3=\>4}}, 3=\>{1=\>{1=\>{1=\>1}}}}

( [http://snippets.dzone.com/posts/show/4146](http://snippets.dzone.com/posts/show/4146) )

Cheers,

j. k.

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [November 4, 2007, 12:39pm UTC](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150/20 "2007-11-04T12:39:18Z")

</div>

Hi –

On Sun, 4 Nov 2007, Ken B. wrote:

> > David
> 
> The Hash.new constructor has several different behaviors depending on  
> what you pass it:

I didn’t say it didn’t. I’m still wondering what you found in my posts  
that was incorrect, if you wouldn’t mind sharing.

David

[Next page](https://www.ruby-forum.com/t/x-x-bla-some-key-does-not-work/119150.md?page=2)
