48

I want to create a Hash in Ruby with default values as an empty array

So, I coded

x = Hash.new([])

However, when I try to push a value into it

x[0].push(99)

All the keys get 99 pushed into that array. How do I resolve this?

Stefan
  • 109,145
  • 14
  • 143
  • 218
Ninjinx
  • 625
  • 2
  • 7
  • 13
  • 2
    `Hash.new { [] }` is what you want. – Cary Swoveland May 21 '15 at 07:48
  • 6
    @CarySwoveland this will return a new array without storing it in the hash – Stefan May 21 '15 at 10:36
  • 1
    you could use `x = Hash.new { [] }` if you were adding values via `|=` (e.g. `x[0] |= [99]`) since that will create a new array and assign it to `x[0]`. I think it's probably better to be explicit about it like in the answers below. – dinjas Sep 12 '18 at 22:57

2 Answers2

89

Lakshmi is right. When you created the Hash using Hash.new([]), you created one array object.

Hence, the same array is returned for every missing key in the Hash.

That is why, if the shared array is edited, the change is reflected across all the missing keys.

Using:

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

Creates and assigns a new array for each missing key in the Hash, so that it is a unique object.

Myst
  • 18,516
  • 2
  • 45
  • 67
  • 1
    What is the difference between `Hash.new(Array.new)` and `Hash.new{Array.new}`? – vildhjarta Oct 31 '19 at 01:21
  • 2
    @vildhjarta `Hash.new(Array.new)` creates the array only once, before passing the new array to the Hash constructor, so all missing objects share that same array. `Hash.new {Array.new}` creates a new array every time the block is called (`{Array.new}`), which produces a new array for every missing object. – Myst Oct 31 '19 at 01:24
33
h = Hash.new{|h,k| h[k] = [] }

h[0].push(99)

This will result in {0=>[99]}


When Hash.new([]) is used, a single object is used as the default value (i.e. value to be returned when a hash key h[0] does not return anything), in this case one array.

So when we say h[0].push(99), it pushes 99 into that array but does not assign h[0] anything. So if you output h you will still see an empty hash {}, while the default object will be [99].


Whereas, when a block is provided i.e. Hash.new{|h,k| h[k] = [] } a new object is created and is assigned to h[k] every time a default value is required.

h[0].push(99) will assign h[0] = [] and push value into this new array.

Lakshmi
  • 351
  • 2
  • 5
  • This is confused. Hash.new([]) creates one object and assigns it to all new entries. Hash.new{[]} creates an object per entry and throws it right away; it is equivalent to Hash.new(nil). – cabo Mar 11 '23 at 17:04