2

Today I tried the following snippets of code and I don't understand why I get different results between them. As far as I can understand they are the same.

One uses the default value off Hash and the other snippet creates an empty array for the key before it'll be accessed.

Anyone who understands what's going on? :)

# Hash default if the key doesn't have a value set is an empty Array
a = Hash.new([])
a[:key] << 2 # => [2]
p a # => {} nil
p a[:key] # => [2]
p a.keys # => []
p a.values # => []

# Explicitly add an array for all nodes before creating
b = Hash.new
b[:key] ||= []
b[:key] << 2 # => [2]
p b # => {:key=>[2]}
p b.keys # => [:key]

Ruby version 1.8.7

gaqzi
  • 3,707
  • 3
  • 30
  • 30
  • 2
    I had the same question a while ago: http://stackoverflow.com/questions/2552579/ruby-method-array-not-updating-the-array-in-hash – Mladen Jablanović Apr 22 '10 at 15:28
  • I don't think they are the same. If I try it with a block, as you did, I can't see the value I put in with `p a[:key]`. But with the default not being a block I do get the value, but it doesn't show unless I call the specific key. With `a.keys` or `a.value` I get an empty `Array` back. Seems like a way to add secret keys to a `Hash`, for whatever reason that'd be good. – gaqzi Apr 22 '10 at 15:59
  • Sorry, not exactly the same. You have changed the default value (try to print `a[:foo]` in your first example and you'll see). I always start with an empty array. BTW, I have asked also this one, seems even more similar: http://stackoverflow.com/questions/1822021/why-does-hash-new-hide-hash-members – Mladen Jablanović Apr 22 '10 at 16:15

2 Answers2

5

Maybe this will help:

a = Hash.new { |hash, key| hash[key] = [] }
a[:key] << 2 # => [2]
a[:key]      # => [2]
p a          # => {:key=>[2]}
maček
  • 76,434
  • 37
  • 167
  • 198
4

When you did a[:key] << 2, you slipped that empty array default value out and added 2 to it (modifying the actual array, not the reference) without letting the hash object a know that you had changed anything. You modified the object that a was using as a default, so you will see this as well:

p a[:wat] #=> [2]
p a[:anything] #=> [2]

In the second example, you made a new array, and use b[:key]= which tells b that it has a value under that key.

Try this if you want the best of both worlds:

c = Hash.new([])
c[:key] += [2]

This will access c[:key] and make a new array with + and reassign it.

mckeed
  • 9,719
  • 2
  • 37
  • 41