4
  1. I make a new hash with a default value of an empty array.

    h = Hash.new([])
    
  2. I push a value into the hash where the key is 'a'.

    h['a'].push(1243)
    
  3. h is empty.

    h # => {} 
    
  4. h['a'] returns the expected value.

    h['a'] # => [1243] 
    
  5. h.keys returns an empty array.

    h.keys # => []
    

If I initialize the hash in step one with Hash.new {|h,k| h[k]=[]} then expected values are returned.

sawa
  • 165,429
  • 45
  • 277
  • 381
dax
  • 10,779
  • 8
  • 51
  • 86
  • 2
    http://stackoverflow.com/questions/2698460/strange-ruby-behavior-when-using-hash-new explains this very well. – fylooi Jun 17 '15 at 10:23
  • 1
    Yes, this is how it works. `Hash.new {|h,k| h[k]=[]}` creates a new array when a key is not found, and `Hash.new([])` uses always the same array when any key is not found. – Eldritch Conundrum Jun 17 '15 at 10:24

4 Answers4

4

Note that all arguments, unlike blocks, are evaluated only once and prior to the method call.

  • In step 1, you are assigning a particular array as the default value. This array instance will be used for the default value of h. Notice that, since you have not set the default value using a block, calling a key-value pair will not assign that to the hash.
  • In step 2, the array instance for the default value is called because 'a' is not a key of the hash. You are modifying this array instance.
  • Steps 3 and 5 are the same thing; since you have not assigned the default value of h with a block, a key-value pair that is called is not assigned to the hash.
  • In step 4, you are just calling the default value.

Compare your code with this:

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

which will generate a new array each time a previously-uncalled key is called, and will assign that key-value pair to the hash.

sawa
  • 165,429
  • 45
  • 277
  • 381
1

Looks like the << operator is appending to the default array itself, instead of working on a copy of it.

 [65] pry(main)> hash = Hash.new([])
=> {}
[66] pry(main)> hash["a"] << 0
=> [0]
[67] pry(main)> hash
=> {}
[68] pry(main)> hash["b"]
=> [0]
[69] pry(main)> hash["c"]
=> [0]

edit: It's not just the << operator, Array#push also pushes to the default array.

fylooi
  • 3,840
  • 14
  • 24
1

Let's look at this from a different angle. You are calling push on an array. Why would calling push on an array modify a hash? There is no relationship between arrays and hashes.

What would you expect to happen here:

a = []

h = Hash.new(a)

a.push(1234)

Would you expect h to change? Probably not. But this is the exact same thing your code does!

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
-1

Cause, when you write h = Hash.new([]) - you create new hash with array as default value for keys that doesn't exist, when you write h['a'].push(1243) - you just add a new element to this array and when you try to do someth like:

h['b'], h['c'], h['lol'] 
=> [1243]

For create new hash you just need to:

1st example:

h = {}
h['a'] = 1243

2nd example

h = Hash.new
h['a'] = 1243
Oleksandr Holubenko
  • 4,310
  • 2
  • 14
  • 28
  • that's true if I use `h['a'] = 1243` but I want to push multiple strings into each key, so it needs to be an array. – dax Jun 17 '15 at 10:21