0
ruby 2.5

我有以下代码:

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

当我在 irb 中运行它时,我得到以下信息:

{"primer"=>"grey", "tan"=>{"yellow"=>{}}, "burgundy"=>{"yellow"=>{}}}

我期待:

{"primer"=>"grey", "tan"=>{"blue"=>{},"yellow"=>{}}, "burgundy"=>{"blue"=>{},"yellow"=>{}}}

为什么第一个 respond_to 产生密钥,而第二个则替换前一个密钥?

我错过了什么?

4

2 回答 2

4

表达方式

test.respond_to?(l)

没有意义。l是一个字符串,respond_to?如果接收者有一个由该字符串表示的名称的方法,则返回 true。由于接收者是一个 Hash 并且一个 Hash 没有方法 Hash#tan 和 Hash#burgundy,所以测试总是会失败。

也许你想做一个test.has_key?(l)......

于 2019-11-13T08:28:22.323 回答
1

假设您有 hash {a: 1},拥有一个键:a不会使 hash 对象响应:ahash.respond_to?(:a)仍然会回来false。你想检查一个键是否存在,这可以使用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

但是,由于您将值设置为哈希值,这是一个真实值。如果当前值是虚假的,您也可以使用||=which 仅分配一个值。test[:non_existing_key]将导致nil(除非设置了默认值)。

意思是上面可以替换为:

layers.each do |l|
  fillers.each do |f|
    test[l] ||= {}
    test[l][f] ||= {}
  end
end

您可以使用productwhich 为您组合两个循环来简化整个语句。

layers.product(fillers) do |layer, filler|
  test[layer] ||= {}
  test[layer][filler] ||= {}
end
于 2019-11-13T14:38:12.220 回答