cgr
cgr

Reputation: 1121

Why the functional differences in Hash initialization?

Is there documentation on the differences of initialization? The docs on Hash didn't have anything that would explain the difference.

foo = [1,2,3,4]
test1 = Hash.new([])
test2 = Hash.new{|h,k| h[k] = []}

foo.each do |i| 
    test1[i] << i 
    test2[i] << i
end

puts "test 1: #{test1.size}" #0
puts "test 2: #{test2.size}" #4

Upvotes: 0

Views: 48

Answers (3)

megas
megas

Reputation: 21791

There's a difference, in some situation it could be significant

test1 = Hash.new([])
test2 = Hash.new{|h,k| h[k] = []}

test1['foo'] #=> []
test2['foo'] #=> []

test1.keys == test2.keys #=> false

The first construction just returns the default value but doesn't do anything with current hash but second construction initialize the hash with key/value where value is calculated by given block.

Upvotes: 1

sawa
sawa

Reputation: 168101

There is mentioning in the doc. Read the doc:

new(obj) → new_hash
new {|hash, key| block } → new_hash

[...] If obj is specified, this single object will be used for all default values. If a block is specified, it will be called with the hash object and the key, and should return the default value. It is the block’s responsibility to store the value in the hash if required.

h = Hash.new("Go Fish")
h["a"] = 100
h["b"] = 200
h["a"]           #=> 100
h["c"]           #=> "Go Fish"
# The following alters the single default object
h["c"].upcase!   #=> "GO FISH"
h["d"]           #=> "GO FISH"
h.keys           #=> ["a", "b"]

# While this creates a new default object each time
h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" }
h["c"]           #=> "Go Fish: c"
h["c"].upcase!   #=> "GO FISH: C"
h["d"]           #=> "Go Fish: d"
h.keys           #=> ["c", "d"]

Upvotes: 3

steenslag
steenslag

Reputation: 80065

This is a common gotcha. With test1 (the non-block) you are modifying the default object, the thing which you get when the key does not exist in the hash.

foo = [1,2,3,4]
test1 = Hash.new([])
test2 = Hash.new{|h,k| h[k] = []}

foo.each do |i| 
    test1[i] << i 
    test2[i] << i
    p test1['doesnotexist'] #added line
end

puts "test 1: #{test1.size}" #0
puts "test 2: #{test2.size}" #4

Output:

[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
test 1: 0
test 2: 4

Upvotes: 1

Related Questions