如何在Ruby中复制哈希?

如何在Ruby中复制哈希?,ruby,serialization,hashmap,copy,deep-copy,Ruby,Serialization,Hashmap,Copy,Deep Copy,我承认我是一个ruby新手(现在正在编写rake脚本)。在大多数语言中,拷贝构造函数很容易找到。半小时的搜索没有在ruby中找到它。我想创建散列的一个副本,以便在不影响原始实例的情况下修改它 某些预期方法无法按预期工作: h0 = { "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"} h1=Hash.new(h0) h2=h1.to_hash 在此期间,我采取了这种不雅观的变通办法 def copyhas

我承认我是一个ruby新手(现在正在编写rake脚本)。在大多数语言中,拷贝构造函数很容易找到。半小时的搜索没有在ruby中找到它。我想创建散列的一个副本,以便在不影响原始实例的情况下修改它

某些预期方法无法按预期工作:

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
的文档说,
initialize\u copy
是覆盖该方法的方法,但是
散列中该方法的链接将引导您改为
replace

该方法是Ruby的标准内置方法,用于执行以下操作:

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

此方法可能具有特定于类的行为。如果是这样,该行为将记录在类的
#initialize_copy
方法下


正如其他人所指出的,
clone
可以做到这一点。请注意,散列的
克隆
会生成浅拷贝。也就是说:

h1 = {:a => 'foo'} 
h2 = h1.clone
h1[:a] << 'bar'
p h2                # => {:a=>"foobar"}
h1={:a=>foo'}
h2=h1.5
h1[:a]{: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"}
def deep_拷贝(o)
封送装载(封送卸载(o))
结束
h1={:a=>'foo'}
h2=深度拷贝(h1)
h1[:a]{:a=>“foo”}
deep\u copy
适用于任何可以编组的对象。大多数内置数据类型(数组、哈希、字符串和c)都可以封送


编组是Ruby的名字。通过编组,对象(及其引用的对象)被转换为一系列字节;然后,这些字节用于创建另一个对象,如原始对象。

您可以使用下面的内容来深度复制哈希对象

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

哈希可以从现有哈希创建新哈希:

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

这是一种特殊情况,但如果您从要获取并复制的预定义哈希开始,则可以创建一个返回哈希的方法:

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

h1 = johns

我的特殊场景是,我有一个JSON模式哈希集合,其中一些哈希是在其他哈希的基础上构建的。我最初将它们定义为类变量,并遇到了这个副本问题。

由于Ruby有一百万种方法可以做到这一点,下面是使用Enumerable的另一种方法:

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

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

克隆速度较慢。为了提高性能,可能应该从空白哈希和合并开始。不包括嵌套哈希的情况

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.030000 3.150000(3.152627)
另一种对我有效的深度复制方式

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

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

如果您使用Rails,您可以执行以下操作:

h1 = h0.deep_dup

我也是Ruby的新手,在复制哈希时也遇到了类似的问题。使用以下命令。我对这种方法的速度一无所知

copy_of_original_hash = Hash.new.merge(original_hash)
如中所述

如果需要反序列化不受信任的数据,请使用JSON或其他方法 只能加载简单“基元”的序列化格式 类型,如字符串、数组、哈希等

下面是一个关于如何在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"}
需要“json”
原创={“约翰”=>“亚当斯”,“托马斯”=>“杰斐逊”,“约翰尼”=>“苹果色”}
克隆=JSON.parse(JSON.generate(原始))
#修改原始散列
原创[“约翰”]{“约翰”=>“亚当斯·桑德勒”、“托马斯”=>“杰斐逊”、“约翰尼”=>“苹果色”}
#克隆的病毒在深度复制时保持原封不动
p克隆
#=>{“约翰”=>“亚当斯”、“托马斯”=>“杰斐逊”、“约翰尼”=>“苹果色”}

这是Wayne Conrad答案的副本。请注意,这与“克隆和复制”有相同的深度复制问题。@forforf是正确的。如果您不了解深度复制和浅层复制,请不要尝试复制数据结构。顺便说一句,克隆是对象上的一种方法,所以任何东西都可以访问它。请参阅API详细信息,在此处为那些没有阅读其他答案的人添加更明确的注释,说明这是一个浅拷贝。#initialize_copy文档似乎不存在用于哈希的文档,即使哈希文档页面上有指向它的链接,对于其他Ruby初学者,“浅拷贝”表示第一级以下的每个对象仍然是引用。请注意,这对我来说不适用于嵌套哈希(如其他答案中所述)。我使用了
Marshal.load(Marshal.dump(h))
。听起来很有希望,但不起作用,这是另一种肤浅的复制。如果您处理的是纯
散列
对象,提供的答案很好。如果您处理的是来自您不控制的地方的哈希对象,则应该考虑是否希望与哈希复制相关联的SuntLon类。请看@K.Carpenter,这不是一份浅薄的副本,与原件共享部分内容吗?据我所知,深度复制是一种不与原件共享任何部分的复制,因此修改一个副本不会修改另一个副本。
Marshal.load(Marshal.dump(o))
深度复制到底是什么?我真的无法理解场景背后发生了什么这也突出了一点,即如果执行
h1[:a]注意:通过封送处理方法进行克隆可能会导致远程代码执行@Jesseldridge True,如果对
封送.load
的输入不可信,请记住一个好的警告。在本例中,它的输入来自我们自己流程中的
Marshal.dump
。我认为在这种情况下,
Marshal.load
是安全的。Rails 3在哈希中深度复制数组方面存在问题。Rails 4修复了这个问题。感谢您指出这一点,我的哈希在使用dup或cloneThis works时仍然受到影响
h1 = h0.deep_dup
copy_of_original_hash = Hash.new.merge(original_hash)
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"}