IT

Ruby에서 해시를 어떻게 복사합니까?

lottoking 2020. 5. 13. 08:23
반응형

Ruby에서 해시를 어떻게 복사합니까?


나는 내가 약간의 루비 초보자임을 인정할 것입니다 (지금 레이크 스크립트 작성). 대부분의 언어에서 사본 생성자를 쉽게 찾을 수 있습니다. 30 분 동안 검색하면 루비에서 찾지 못했습니다. 원본 인스턴스에 영향을 미치지 않고 해시 복사본을 만들려고합니다.

의도 한대로 작동하지 않는 일부 예상되는 방법 :

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"}

동작이 재정의 될 수 있습니다.

이 메서드에는 클래스 별 동작이있을 수 있습니다. 그렇다면, 그 행동은 #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마샬링 될 수있는 모든 개체에 적용됩니다. 대부분의 기본 제공 데이터 형식 (Array, Hash, String 등)을 마샬링 할 수 있습니다.

Marshalling 은 루비의 직렬화 이름입니다 . 마샬링을 사용하면 객체가 참조하는 객체가 일련의 바이트로 변환됩니다. 그런 다음 해당 바이트는 원본과 같은 다른 객체를 만드는 데 사용됩니다.


Rails를 사용하는 경우 다음을 수행 할 수 있습니다.

h1 = h0.deep_dup

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


해시는 기존 해시에서 새 해시를 만들 수 있습니다.

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

I am also a newbie to Ruby and I faced similar issues in duplicating a hash. Use the following. I've got no idea about the speed of this method.

copy_of_original_hash = Hash.new.merge(original_hash)

As mentioned in Security Considerations section of Marshal documentation,

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.

Here is an example on how to do cloning using JSON in Ruby:

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"}

Use 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...)


you can use below to deep copy Hash objects.

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

Since standard cloning method preserves the frozen state, it is not suitable for creating new immutable objects basing on the original object, if you would like the new objects be slightly different than the original (if you like stateless programming).


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:

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

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. I was initially defining them as class variables and ran into this copy issue.


Alternative way to Deep_Copy that worked for me.

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.

참고URL : https://stackoverflow.com/questions/4157399/how-do-i-copy-a-hash-in-ruby

반응형