Reputation: 6639
ruby 2.5
I have the following code:
test = {'primer' => 'grey'}
layers = ["tan","burgundy"]
fillers = ["blue","yellow"]
layers.each do |l|
fillers.each do |f|
test[l] = {} if !test.respond_to?(l)
test[l][f] = {} if !test[l].respond_to?(f)
end
end
When I run it in irb, I get the following:
{"primer"=>"grey", "tan"=>{"yellow"=>{}}, "burgundy"=>{"yellow"=>{}}}
I am expecting:
{"primer"=>"grey", "tan"=>{"blue"=>{},"yellow"=>{}}, "burgundy"=>{"blue"=>{},"yellow"=>{}}}
Why does the first respond_to produce the key, when the second one, replaces the previous key?
What am I missing?
Upvotes: 0
Views: 52
Reputation: 21130
Let's say you have the hash {a: 1}
, having a key :a
doesn't make the hash object respond to :a
. hash.respond_to?(:a)
would still return false
. You want to check if a key exists, this can be done using has_key?
/key?
.
layers.each do |l|
fillers.each do |f|
test[l] = {} unless test.has_key?(l)
test[l][f] = {} unless test[l].has_key?(f)
end
end
However since you set the values to a hash, which is a truthy value. You could also use ||=
which only assigns a value if the current value is falsy. test[:non_existing_key]
will result in nil
(unless a default is set).
Meaning the above can be replaced with:
layers.each do |l|
fillers.each do |f|
test[l] ||= {}
test[l][f] ||= {}
end
end
You could simplify this whole statement with the use of product
which combines the two loops for you.
layers.product(fillers) do |layer, filler|
test[layer] ||= {}
test[layer][filler] ||= {}
end
Upvotes: 1
Reputation: 22217
The expression
test.respond_to?(l)
does not make sense. l
is a string, and respond_to?
returns true if the receiver has a method of the name represented by this string. Since the receiver is a Hash and a Hash has no methods Hash#tan and Hash#burgundy, the test will always fail.
Maybe you want to do a test.has_key?(l)
instead....
Upvotes: 4