0

I want to have a hash whose key is a string and the value is an array. I tried it the following way:

h = Hash.new([]) # => {} 
h["one"]         # => [] 
h["two"]         # => [] 
h["one"].push 1  # => [1] 
h["one"]         # => [1] 
h["two"]         # => [1] //why did it assign it to h["two"] also??

What is the correct way to do it?

sawa
  • 165,429
  • 45
  • 277
  • 381
user3156792
  • 79
  • 1
  • 7
  • That is the correct way to do it. – sawa Apr 23 '14 at 10:03
  • possible duplicate of [Can't use an array as default values for Ruby Hash?](http://stackoverflow.com/questions/5488853/cant-use-an-array-as-default-values-for-ruby-hash) – Uri Agassi Apr 23 '14 at 11:00

2 Answers2

4

You get this behavior because [] that you passed into new method isn't copied but referenced in all unset hash keys. So h['one'] references the same object as h['two']. So if you modify object referenced by h['one'] (using push method), h['two'] will also be modified.

The correct way to set default value which will be initialized for every single hash key is to use block:

h = Hash.new { |hash, key| hash[key] = [] }
Marek Lipka
  • 50,622
  • 7
  • 87
  • 91
3

I usually do it like this:

h = Hash.new { |h,k| h[k] = [] }
h['one']
h
# => { 'one' => [] }
h['two'] << 12
h
# => { 'one' => [], 'two' => [12] }

Which is more verbose and (IMO) reads nicer.

xlembouras
  • 8,215
  • 4
  • 33
  • 42