EastsideDev
EastsideDev

Reputation: 6639

Ruby key getting replaced, instead of a new key created

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

Answers (2)

3limin4t0r
3limin4t0r

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

user1934428
user1934428

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

Related Questions