2

I wanted to use Ruby's default hash values to allow me to more easily nest hashes without having to manually initialize them. I thought it'd be nice to be able to dig a level down for each key safely without having pre-set the key as a hash. However, I find that when I do this, the data gets stored somewhere, but is not visible by accessing the top-level hash. Where does it go, and how does this work?

top = Hash.new({})               #=> {}
top[:first][:thing] = "hello"    #=> "hello"
top[:second] = {thing: "world"}  #=> {:thing => "world"}
top                              #=> {:second => {:thing => "world"}}
top[:first]                      #=> {:thing => "hello"}
Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
jack_was_taken
  • 939
  • 1
  • 7
  • 17

3 Answers3

3

You want to know where your inserted hash is? Maybe you have heard about Schroedingers cat:

h = Hash.new({})
h[:box][:cat] = "Miau"
=>  "Miau"
h
=> {}

The cat seem to be dead....

h[:schroedingers][:cat]
=> "Miau"

The cat seem still to be alive, but in a different reality....

Ok, if nothing helps, "Read The Fine Manual". For Hash.new, we read:

If obj is specified, this single object will be used for all default values.

So when you write h[:box], a object is returned, and this object is another hash, and it happen to empty.

Into this empty hash, you write an key-value.

Now this other hash is no longer empty, it has a key-value pair. And it is returned every time you search for a key is not found in your original hash.

Meier
  • 3,858
  • 1
  • 17
  • 46
2

You can access the default value via a variety of #default methods http://ruby-doc.org/core-2.2.3/Hash.html#method-i-default

top.default
=> {:thing=>"hello"}
Jeff Price
  • 3,229
  • 22
  • 24
  • Wow, totally unexpected that this would be updating the default rather than setting the value at the provided key. But sure, understood, thanks~ – jack_was_taken Oct 26 '15 at 20:39
  • 1
    There can only be one default. Whether you default is "1" or {}, that exact object will be used as the default. Bassel in his answer shows how to pass in a block as the default value and have that return a new distinct hash. In his case, the code block is the "one true default". – Jeff Price Oct 26 '15 at 20:50
2

You can also tell it how you want it to act, example:

irb(main):058:0> top = Hash.new {|h,k| h[k] = {}; h[k]}
=> {}
irb(main):059:0> top[:first][:thing] = "hello"
=> "hello"
irb(main):060:0> top[:second] = {thing: "world"}
=> {:thing=>"world"}
irb(main):061:0> top
=> {:first=>{:thing=>"hello"}, :second=>{:thing=>"world"}}
Bassel Samman
  • 802
  • 5
  • 11