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.
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.
All being said, check Enumerable#group_by:
["abc", "def", "four", "five"].group_by(&:length)
#=> {3=>["abc", "def"], 4=>["four", "five"]}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With