12

I tried the following ruby code, which I thought would return a hash of word lengths to the words with those lengths. Instead it is empty.

map = Hash.new(Array.new)    
strings = ["abc","def","four","five"]
strings.each do |word|
  map[word.length] << word  
end   

However, if I modify it to

map = Hash.new
strings = ["abc","def","four","five"]
strings.each do |word|
  map[word.length] ||= []
  map[word.length] << word  
end

It does work.

Doesn't the first version just create a hash whose default values are an empty array? In this case, I don't understand why the 2 blocks give different values.

Jeff Storey
  • 56,312
  • 72
  • 233
  • 406

3 Answers3

30

The problem is that you aren't actually assigning anything to the hash keys, you're just using the << operator to modify the existing contents of a default value. Since you don't assign anything to the hash key, it is not added. In fact, you'll notice the default value is the one modified:

h = Hash.new []
p h[0]           # []
h[0] << "Hello"
p h              # {}
p h[0]           # ["Hello"]
p h[1]           # ["Hello"]

This is because the same Array object is maintained as the default value. You can fix it by using the + operator, though it may be less efficient:

map = Hash.new []
strings = ["abc", "def", "four", "five"]

strings.each do |word|
    map[word.length] += [word]
end

And now it works as expected.

Ry-
  • 218,210
  • 55
  • 464
  • 476
  • Helped me a lot, thanks! This didn't work for me however until i changed the `word` after `+=` to be an array, like `[word]` – Subtletree Dec 27 '13 at 11:50
12

All being said, check Enumerable#group_by:

["abc", "def", "four", "five"].group_by(&:length)
#=> {3=>["abc", "def"], 4=>["four", "five"]}
tokland
  • 66,169
  • 13
  • 144
  • 170
2

I think what the first version really means is that the default value is only one array. The second example explicitly creates a new array if one doesn't exist yet.

This looks like a good read to further clarify.

Benjamin Tan Wei Hao
  • 9,621
  • 3
  • 30
  • 56