如何在Ruby中复制散列?

我承认,我是一个ruby新手(现在写耙脚本)。 在大多数语言中,复制构造函数很容易find。 半个小时的search没有发现ruby。 我想创build一个散列的副本,以便我可以修改它,而不会影响原始实例。

一些预期的方法不能按预期工作:

h0 = { "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"} h1=Hash.new(h0) h2=h1.to_hash 

与此同时,我采取了这个不起眼的解决办法

 def copyhash(inputhash) h = Hash.new inputhash.each do |pair| h.store(pair[0], pair[1]) end return h end 

clone方法是Ruby的标准,内置的方式来做一个浅拷贝 :

 irb(main):003:0> h0 = {"John" => "Adams", "Thomas" => "Jefferson"} => {"John"=>"Adams", "Thomas"=>"Jefferson"} irb(main):004:0> h1 = h0.clone => {"John"=>"Adams", "Thomas"=>"Jefferson"} irb(main):005:0> h1["John"] = "Smith" => "Smith" irb(main):006:0> h1 => {"John"=>"Smith", "Thomas"=>"Jefferson"} irb(main):007:0> h0 => {"John"=>"Adams", "Thomas"=>"Jefferson"} 

请注意,该行为可能被覆盖:

此方法可能具有类特定的行为。 如果是这样,这个行为将被logging在#initialize_copy方法下。

正如其他人指出的, clone将做到这一点。 请注意,哈希的clone会生成一个浅表副本。 也就是说:

 h1 = {:a => 'foo'} h2 = h1.clone h1[:a] << 'bar' p h2 # => {:a=>"foobar"} 

发生什么事是散列的引用被复制,但不是引用引用的对象。

如果你想要一个深层复制,那么:

 def deep_copy(o) Marshal.load(Marshal.dump(o)) end h1 = {:a => 'foo'} h2 = deep_copy(h1) h1[:a] << 'bar' p h2 # => {:a=>"foo"} 

deep_copy适用于可以编组的任何对象。 大多数内置的数据types(数组,哈希,string等)可以编组。

编组是Ruby的序列化的名字。 通过编组,将对象与其引用的对象一起转换为一系列字节; 这些字节然后用于创build另一个对象,如原来的。

如果你使用的是Rails,你可以这样做:

 h1 = h0.deep_dup 

http://apidock.com/rails/Hash/deep_dup

哈希可以从现有的哈希创build一个新的哈希:

 irb(main):009:0> h1 = {1 => 2} => {1=>2} irb(main):010:0> h2 = Hash[h1] => {1=>2} irb(main):011:0> h1.object_id => 2150233660 irb(main):012:0> h2.object_id => 2150205060 

我也是Ruby的新手,我在复制散列时遇到类似的问题。 使用以下内容。 我不知道这种方法的速度。

 copy_of_original_hash = Hash.new.merge(original_hash) 

使用Object#clone

 h1 = h0.clone 

(令人困惑的是, clone的文档说initialize_copy是覆盖这个的方法,但是在Hash该方法的链接指示你replace …)

您可以使用下面的方法深入复制哈希对象。

 deeply_copied_hash = Marshal.load(Marshal.dump(original_hash)) 

正如Marshal文档的安全注意事项部分所述 ,

如果您需要反序列化不可信数据,请使用JSON或另一种只能加载简单的“原始”types(例如String,Array,Hash等)的序列化格式。

下面是一个关于如何在Ruby中使用JSON进行克隆的例子:

 require "json" original = {"John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"} cloned = JSON.parse(JSON.generate(original)) # Modify original hash original["John"] << ' Sandler' p original #=> {"John"=>"Adams Sandler", "Thomas"=>"Jefferson", "Johny"=>"Appleseed"} # cloned remains intact as it was deep copied p cloned #=> {"John"=>"Adams", "Thomas"=>"Jefferson", "Johny"=>"Appleseed"} 

由于标准克隆方法保留了冻结状态,如果您希望新对象与原始对象稍有不同(如果您喜欢无状态编程),则不适合基于原始对象创build新的不可变对象。

克隆很慢。 对于性能应该可能开始与空白散列和合并。 不包括嵌套哈希的情况…

 require 'benchmark' def bench Benchmark.bm do |b| test = {'a' => 1, 'b' => 2, 'c' => 3, 4 => 'd'} b.report 'clone' do 1_000_000.times do |i| h = test.clone h['new'] = 5 end end b.report 'merge' do 1_000_000.times do |i| h = {} h['new'] = 5 h.merge! test end end b.report 'inject' do 1_000_000.times do |i| h = test.inject({}) do |n, (k, v)| n[k] = v; n end h['new'] = 5 end end end end 

  长凳用户系统总数(实际)
  克隆1.960000 0.080000 2.040000(2.029604)
  合并1.690000 0.080000 1.770000(1.767828)
  注射3.120000 0.030000 3.150000(3.152627)
  

由于Ruby有一百万种方法来实现它,下面是使用Enumerable的另一种方法:

 h0 = { "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"} h1 = h0.inject({}) do |new, (name, value)| new[name] = value; new end 

这是一个特殊的情况,但是如果你从一个预定义的散列开始,你想要获得一个副本,你可以创build一个返回散列的方法:

 def johns { "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"} end h1 = johns 

我所特有的场景是我收集了一些JSON模式哈希,其中一些哈希使用了其他哈希。 我最初将它们定义为类variables,并遇到了这个复制问题。

Deep_Copy的替代方法,为我工作。

 h1 = {:a => 'foo'} h2 = Hash[h1.to_a] 

这产生了一个deep_copy,因为h2是使用h1的数组表示形成的,而不是h1的引用。