将一个哈希复制到另一个哈希时的奇怪行为

时间:2017-03-08 18:23:30

标签: ruby

我有一个管理配置的类(本地保存为JSON),我编写了一个方法将配置从一个文件复制到另一个文件。两个JSON文件都作为哈希加载到实例变量中(分别为@local@repository)。我遇到了一些奇怪的行为,在一个哈希上设置一个键就会覆盖另一个哈希中相同键的值。我已将问题缩小到下面代码片段的第14行。第12行的puts语句将@repository[brand][:branches][branch]显示为包含数据的非空哈希,而第21行的puts语句将@repository[brand][:branches][branch]显示为空哈希。< / p>

def copy(brand, branch = nil)
    brand = brand.to_sym
    branch =  branch.to_sym

    if branch.nil?
        if @local[:repository].has_key?(brand)
            @local[:repository][brand].deep_merge(@repository[brand])
        else
            @local[:repository][brand] = @repository[brand]
        end
    else
        puts @repository[brand][:branches][branch]
        unless @local[:repository].has_key?(brand)
            @local[:repository][brand] = @repository[brand]
            @local[:repository][brand][:branches] = Hash.new
        end

        unless @local[:repository][brand][:branches].has_key?(branch)
            @local[:repository][brand][:branches][branch] = Hash.new
        end
        puts @repository[brand][:branches][branch]
        @local[:repository][brand][:branches][branch].deep_merge(@repository[brand][:branches][branch])
    end

    self.write(CONFIG_FILE, @local)
end

如果我将第14行更改为@local[:repository][brand] = Hash.new,则第21行上该哈希的值不再为空,实际上是预期值。一个关键是brand中的@local[:repository]键尚不存在。

有人能说清楚这里发生了什么吗?

1 个答案:

答案 0 :(得分:2)

罪魁祸首在于这一行(你使用了两次):

@local[:repository][brand] = @repository[brand]

这不会复制驻留在@repository[brand]中的哈希。
让我们把它分开一点,这样我们就可以更清楚地谈论它了:

brand_detail = @repository[brand]
local_repo = @local[:repository]
local_repo[brand] = brand_detail

在第三个语句之后,local_repo[brand]将不包含brand_details哈希的副本,而是引用到该哈希。所以现在所有这些线都会产生同样的效果;他们都会修改相同的哈希实例:

  • brand_detail[:branches] = 3
  • local_repo[brand][:branches] = 3
  • @repository[brand][:branches] = 3

您应该能够通过使用dup明确添加副本而不是引用来规避这一点:

@local[:repository][brand] = @repository[brand].dup