【问题标题】:Ruby dup/clone recursivelyRuby dup/clone 递归
【发布时间】:2012-02-01 09:31:22
【问题描述】:

我有一个像这样的哈希:

h = {'name' => 'sayuj', 
     'age' => 22, 
     'project' => {'project_name' => 'abc', 
                   'duration' => 'prq'}}

我需要这个哈希的副本,更改不应影响原始哈希。

当我尝试时,

d = h.dup # or d = h.clone
d['name'] = 'sayuj1'
d['project']['duration'] = 'xyz'

p d #=> {"name"=>"sayuj1", "project"=>{"duration"=>"xyz", "project_name"=>"abc"}, "age"=>22}
p h #=> {"name"=>"sayuj", "project"=>{"duration"=>"xyz", "project_name"=>"abc"}, "age"=>22}

在这里您可以看到 project['duration'] 在原始哈希中发生了更改,因为 project 是另一个哈希对象。

我希望哈希是 dupedcloned 递归。我怎样才能做到这一点?

【问题讨论】:

    标签: ruby hash clone hash-of-hashes dup


    【解决方案1】:

    这是在 Ruby 中制作深拷贝的方法

    d = Marshal.load( Marshal.dump(h) )
    

    【讨论】:

    • 这会创建h 引用的所有对象的完整副本。这可能正是 Sayuj 对简单字符串散列所需要的。对于更复杂的对象,这可能不再需要了。一旦可以覆盖Hash#dup 方法以递归地复制values 中的所有哈希值。但这需要针对每种对象类型进行扩展。
    • @HolgerJust:是的,这就是为什么它被称为“深拷贝”:-)
    • 当然。我只是想提一下,它可能比 OP 的预期做得更多(尽管它可能还不错):) 所以它只是为了将来参考。
    • 请注意,当有默认 proc 时,这将不起作用(例如 h = Hash.new {|h,k| h[k] = 1}
    【解决方案2】:

    如果你在 Rails 中:Hash.deep_dup

    【讨论】:

      【解决方案3】:

      如果Marchal #dump/load 对不起作用,对于 有一个Hash 的方法#deep_dup,所以你可以:

      h = {'name' => 'sayuj', 
       'age' => 22, 
       'project' => {'project_name' => 'abc', 
                     'duration' => 'prq'}}
      
      h1 = h.deep_dup
      

      【讨论】:

      • 方法应该是h.deep_dup而不是h.deep.dup
      • deep_dup 方法会将一个类变成匿名类,不推荐。
      • @TianChen 例子?
      • 看这个例子:class Abc; endh = { 'class' => Abc }h1 = h.deep_dup # => => {"class"=>#<Class:0x0000000abe50a0>}
      【解决方案4】:

      这是一个相当老的问题的答案,但我在实现类似的东西时偶然发现了它,我想我会加入一个更有效的方法。

      对于像上面这样简单的两级深度哈希,你也可以这样做:

      d = h.inject({}) {|copy, (key, value)| 
          copy[key] = value.dup rescue value; copy
      }
      

      我对包含 4k 个元素的散列进行了测试,每个元素为几百字节,它比 Marshal.dump/load 快 50%

      当然,它并不完整,因为如果你有一个散列,例如“project_name”字段的值,它就不起作用,但是对于一个简单的 2 级散列,它工作得很好/更快。

      【讨论】:

        【解决方案5】:

        另一种选择是使用处理数组、哈希、结构并且可扩展到用户定义的类的 full_dup gem(完全公开:我是该 gem 的作者)。

        使用方法:

        require 'full_dup'
        # Other code omitted ...
        d = h.full_dup
        

        另请注意,full_dup 处理复杂的数据关系,包括循环或递归。

        【讨论】:

          猜你喜欢
          • 2018-08-16
          • 2012-04-28
          • 1970-01-01
          • 1970-01-01
          • 2019-06-18
          • 2012-07-29
          • 2012-08-10
          • 1970-01-01
          • 2015-07-17
          相关资源
          最近更新 更多