在Ruby2中克隆哈希

时间:2013-12-03 14:45:50

标签: ruby

我试图克隆一个哈希,以创建原始哈希的新副本,但似乎当我在新哈希中设置一个值时,我对原始哈希有相同的效果。

rr = Hash.new
command = "/usr/local/bin/aws route53 list-resource-record-sets --hosted-zone-id EXAMPLEID --max-items 1"

rr=JSON.parse(%x{#{command}})

puts rr

if rr["ResourceRecordSets"][0]["TTL"] != 60
  new_rr = rr.clone
  new_rr["ResourceRecordSets"][0]["TTL"] = 60
  puts rr
  puts new_rr
end

输出:

{"NextRecordType"=>"MX", "NextRecordName"=>"example.com.", "ResourceRecordSets"=>[{"ResourceRecords"=>[{"Value"=>"1.2.3.4"}], "Type"=>"A", "Name"=>"example.com.", "TTL"=>1800}], "MaxItems"=>"1", "IsTruncated"=>true}
{"NextRecordType"=>"MX", "NextRecordName"=>"example.com.", "ResourceRecordSets"=>[{"ResourceRecords"=>[{"Value"=>"1.2.3.4"}], "Type"=>"A", "Name"=>"example.com.", "TTL"=>60}], "MaxItems"=>"1", "IsTruncated"=>true}
{"NextRecordType"=>"MX", "NextRecordName"=>"example.com.", "ResourceRecordSets"=>[{"ResourceRecords"=>[{"Value"=>"1.2.3.4"}], "Type"=>"A", "Name"=>"example.com.", "TTL"=>60}], "MaxItems"=>"1", "IsTruncated"=>true}

我没有看到Ruby 2.0中记录的Hash.clone,我现在应该使用其他方法来创建Hash副本吗?

提前致谢。

2 个答案:

答案 0 :(得分:4)

哈希是键和值的集合,其中值是对象的引用。复制哈希时,正在创建新哈希,但正在复制所有对象引用,因此您将获得包含相同值的新哈希。这就是为什么这会起作用的原因:

hash = {1 => 'Some string'} #Strings are mutable
hash2 = hash.clone

hash2[1] #=> 'Some string'
hash2[1].upcase!            # modifying mutual object
hash[1] #=> 'SOME STRING;   # so it appears modified on both hashes
hash2[1] = 'Other string'   # changing reference on second hash to another object
hash[1] #=> 'SOME STRING'   # original obejct has not been changed

hash2[2] = 'new value'      # adding obejct to original hash
hash[2] #=> nil

如果要复制引用的对象,则需要执行深层复制。它作为deep_dup方法添加到rails(activesupport gem)中。如果你没有使用rails并且不想安装gem,你可以像下面这样写:

class Hash
  def deep_dup
    Hash[map {|key, value| [key, value.respond_to?(:deep_dup) ? value.deep_dup : begin 
        value.dup
      rescue
        value
      end]}]
  end
end

hash = {1 => 'Some string'} #Strings are mutable
hash2 = hash.deep_dup

hash2[1] #=> 'Some string'
hash2[1].upcase!            # modifying referenced object
hash2[1] #=> 'SOME STRING'
hash[1] #=> 'Some string;   # now other hash point to original object's clone

您可能应该为数组编写类似的内容。我也想过为整个可枚举的模块编写它,但它可能会稍微复杂一点。

答案 1 :(得分:1)

制作大多数Ruby对象(包括字符串,数组,散列及其组合)的深层副本的最简单方法是使用Marshal

def deep_copy(obj)
  Marshal.load(Marshal.dump(obj))
end

例如,

h        = {a: 1, b: [:c, d: {e: 4}]} # => {:a=>1, :b=>[:c, {:d=>{:e=>4}}]}
hclone   = h.clone
hdup     = h.dup
hmarshal = deep_copy(h)

h[:b][1][:d][:e] = 5
h        # => {:a=>1, :b=>[:c, {:d=>{:e=>5}}]}
hclone   # => {:a=>1, :b=>[:c, {:d=>{:e=>5}}]}
hdup     # => {:a=>1, :b=>[:c, {:d=>{:e=>5}}]}
hmarshal # => {:a=>1, :b=>[:c, {:d=>{:e=>4}}]}