Ruby dup/clone 递归
Posted
技术标签:
【中文标题】Ruby dup/clone 递归【英文标题】:Ruby dup/clone recursively 【发布时间】: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
是另一个哈希对象。
我希望哈希是 duped
或 cloned
递归。我怎样才能做到这一点?
【问题讨论】:
【参考方案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
对不起作用,对于rails 有一个Hash
的方法#deep_dup
,所以你可以:
h = 'name' => 'sayuj',
'age' => 22,
'project' => 'project_name' => 'abc',
'duration' => 'prq'
h1 = h.deep_dup
【讨论】:
方法应该是h.deep_dup而不是h.deep.dupdeep_dup
方法会将一个类变成匿名类,不推荐。
@TianChen 例子?
看这个例子:class Abc; end
h = '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 处理复杂的数据关系,包括循环或递归。
【讨论】:
以上是关于Ruby dup/clone 递归的主要内容,如果未能解决你的问题,请参考以下文章