简体   繁体   English

如何在 Ruby 中复制哈希?

[英]How do I copy a hash in Ruby?

I'll admit that I'm a bit of a ruby newbie (writing rake scripts, now).我承认我有点像红宝石新手(现在正在编写 rake 脚本)。 In most languages, copy constructors are easy to find.在大多数语言中,复制构造函数很容易找到。 Half an hour of searching didn't find it in ruby.搜索了半个小时,没有在ruby中找到。 I want to create a copy of the hash so that I can modify it without affecting the original instance.我想创建哈希的副本,以便可以在不影响原始实例的情况下对其进行修改。

Some expected methods that don't work as intended:一些无法按预期工作的预期方法:

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

In the meantime, I've resorted to this inelegant workaround与此同时,我采用了这种不雅的解决方法

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

The clone method is Ruby's standard, built-in way to do a shallow-copy : clone方法是 Ruby 标准的、内置的浅拷贝方法:

h0 = {"John" => "Adams", "Thomas" => "Jefferson"}
# => {"John"=>"Adams", "Thomas"=>"Jefferson"}
h1 = h0.clone
# => {"John"=>"Adams", "Thomas"=>"Jefferson"}
h1["John"] = "Smith"
# => "Smith"
h1
# => {"John"=>"Smith", "Thomas"=>"Jefferson"}
h0
# => {"John"=>"Adams", "Thomas"=>"Jefferson"}

Note that the behavior may be overridden:请注意,该行为可能会被覆盖:

This method may have class-specific behavior.此方法可能具有特定于类的行为。 If so, that behavior will be documented under the #initialize_copy method of the class.如果是这样,该行为将记录在类的#initialize_copy方法下。

As others have pointed out, clone will do it.正如其他人指出的那样, clone会做到这一点。 Be aware that clone of a hash makes a shallow copy.请注意,哈希的clone会产生浅拷贝。 That is to say:也就是说:

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

What's happening is that the hash's references are being copied, but not the objects that the references refer to.发生的事情是哈希的引用被复制了,而不是引用引用的对象。

If you want a deep copy then:如果你想要一个深拷贝,那么:

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 works for any object that can be marshalled. deep_copy适用于任何可以编组的对象。 Most built-in data types (Array, Hash, String, &c.) can be marshalled.大多数内置数据类型(数组、哈希、字符串等)都可以编组。

Marshalling is Ruby's name for serialization . Marshalling是 Ruby 对序列化的称呼。 With marshalling, the object--with the objects it refers to--is converted to a series of bytes;通过编组,对象——连同它所指的对象——被转换为一系列字节; those bytes are then used to create another object like the original.然后使用这些字节创建另一个像原始对象一样的对象。

If you're using Rails you can do:如果你使用 Rails,你可以这样做:

h1 = h0.deep_dup

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

Hash can create a new hash from an existing hash: 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

As mentioned in Security Considerations section of Marshal documentation ,正如Marshal 文档的安全注意事项部分所述

If you need to deserialize untrusted data, use JSON or another serialization format that is only able to load simple, 'primitive' types such as String, Array, Hash, etc.如果您需要反序列化不受信任的数据,请使用 JSON 或其他只能加载简单“原始”类型(如字符串、数组、哈希等)的序列化格式。

Here is an example on how to do cloning using JSON in Ruby:这是一个关于如何在 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"}

I am also a newbie to Ruby and I faced similar issues in duplicating a hash.我也是 Ruby 的新手,我在复制哈希时遇到了类似的问题。 Use the following.使用以下内容。 I've got no idea about the speed of this method.我不知道这种方法的速度。

copy_of_original_hash = Hash.new.merge(original_hash)

Use Object#clone :使用Object#clone

h1 = h0.clone

(Confusingly, the documentation for clone says that initialize_copy is the way to override this, but the link for that method in Hash directs you to replace instead...) (令人困惑的是, clone的文档说initialize_copy是覆盖它的方法,但是Hash中该方法的链接会指示您replace ...)

This is a special case, but if you're starting with a predefined hash that you want to grab and make a copy of, you can create a method that returns a hash:这是一种特殊情况,但如果您从要获取并复制的预定义哈希开始,您可以创建一个返回哈希的方法:

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

h1 = johns

The particular scenario that I had was I had a collection of JSON-schema hashes where some hashes built off others.我遇到的特殊情况是我有一组 JSON 模式哈希,其中一些哈希建立在其他哈希之上。 I was initially defining them as class variables and ran into this copy issue.我最初将它们定义为类变量并遇到了这个复制问题。

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

Clone is slow.克隆很慢。 For performance should probably start with blank hash and merge.为了提高性能,可能应该从空白哈希和合并开始。 Doesn't cover case of nested hashes...不包括嵌套哈希的情况......

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
bench  user      system      total        ( real)
  clone  1.960000   0.080000    2.040000    (  2.029604)
  merge  1.690000   0.080000    1.770000    (  1.767828)
  inject 3.120000   0.030000    3.150000    (  3.152627)

Since Ruby has a million ways to do it, here's another way using Enumerable:由于 Ruby 有一百万种方法可以做到这一点,这里有另一种使用 Enumerable 的方法:

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

您可以使用下面的深度复制 Hash 对象。

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

Alternative way to Deep_Copy that worked for me.对我有用的 Deep_Copy 的替代方法。

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

This produced a deep_copy since h2 is formed using an array representation of h1 rather than h1's references.这产生了一个 deep_copy,因为 h2 是使用 h1 的数组表示而不是 h1 的引用形成的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM