如何在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另一个对象,如原来的。
哈希可以从现有的哈希创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的引用。