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


197

나는 내가 약간의 루비 초보자임을 인정할 것입니다 (지금 레이크 스크립트 작성). 대부분의 언어에서 복사 생성자를 쉽게 찾을 수 있습니다. 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

평범한 Hash물건을 다루는 경우 제공된 대답이 좋습니다. 제어하지 않는 장소에서 나오는 해시와 유사한 객체를 처리하는 경우 해시와 관련된 싱글 톤 클래스를 복제할지 여부를 고려해야합니다. 참조 stackoverflow.com/questions/10183370/...

답변:


223

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클래스 의 방법 으로 문서화 될 것 입니다.


클론은 Object, BTW의 방법이므로 모든 것이 접근 할 수 있습니다. API 세부 정보는 여기를
Dylan Lacey

29
얕은 답변을하는 다른 답변을 읽지 않는 사람들을 위해 여기에 더 명확한 주석을 추가하십시오.
grumpasaurus


14
그리고 다른 루비 초보자들에게 "얕은 복사"는 첫 번째 레벨 아래의 모든 객체가 여전히 참조임을 의미합니다.
RobW

9
이것은 다른 답변에서 언급했듯이 중첩 해시에는 작동하지 않습니다. 나는 사용했다 Marshal.load(Marshal.dump(h)).
bheeshmar

178

다른 사람들이 지적했듯이 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 은 루비의 직렬화 이름입니다 . 마샬링을 사용하면 객체가 참조하는 객체가 일련의 바이트로 변환됩니다. 그런 다음 해당 바이트는 원본과 같은 다른 객체를 만드는 데 사용됩니다.


딥 카피에 대한 정보를 제공 한 것이 좋지만 의도하지 않은 부작용 (예 : 해시를 수정하면 둘 다 수정)이 발생할 수 있다는 경고가 표시됩니다. 해시 복제의 주요 목적은 원본의 수정 (불변성 등)을 방지하는 것입니다.
K. Carpenter

6
@ K.Carpenter 원본의 일부를 공유 하는 얕은 사본이 아닙니까? 딥 카피는 내가 이해하는 것처럼 원본의 일부를 공유하지 않는 사본이므로 하나를 수정해도 다른 사본은 수정되지 않습니다.
Wayne Conrad

1
Marshal.load(Marshal.dump(o))정밀 복사 는 얼마나 정확 합니까? 정말 무대 뒤에서 일어나는 이해할 수 없다
Muntasir 알람을

이것이 강조 h1[:a] << 'bar'하는 것은 원래 객체 (h1 [: a]로 가리키는 문자열)를 수정하면 h1[:a] = "#{h1[:a]}bar"대신 새 문자열 객체를 만들고 h1[:a]그것을 가리킬 것 h2[:a]입니다. 여전히 오래된 (수정되지 않은) 문자열을 가리 킵니다.
Max Williams

@MuntasirAlam 나는 마샬링이하는 것에 대해 몇 마디를 추가했습니다. 도움이 되길 바랍니다.
Wayne Conrad

73

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

h1 = h0.deep_dup

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


2
Rails 3에는 Hashes 내의 deep_duping 배열에 문제가 있습니다. Rails 4에서이 문제를 해결했습니다.
pdobb

1
DUP 또는 복제 사용할 경우 지점이 알아 주셔서 감사합니다, 내 해시 여전히 영향을 받았습니다
Esgi Dendyanri

13

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

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

24
이것은 #clone 및 #dup과 동일한 딥 카피 문제가 있습니다.
forforf

3
@forforf가 정확합니다. 깊은 복사와 얕은 복사를 이해하지 못하는 경우 데이터 구조를 복사하지 마십시오.
James Moore

5

나는 또한 Ruby의 초보자이며 해시 복제와 비슷한 문제에 직면했습니다. 다음을 사용하십시오. 나는이 방법의 속도에 대해 전혀 모른다.

copy_of_original_hash = Hash.new.merge(original_hash)

3

에서 언급 한 바와 같이 원수 문서의 보안 고려 사항 섹션 ,

신뢰할 수없는 데이터를 역 직렬화해야하는 경우 JSON 또는 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"}

1

사용 Object#clone:

h1 = h0.clone

(혼란스럽게도에 대한 문서 cloneinitialize_copy이것을 무시하는 방법 이라고 말하지만 그 방법에 대한 링크는 대신에 Hash당신을 지시합니다 replace...)


1

표준 복제 방법은 고정 상태를 유지하므로 새 객체가 원본과 약간 다른 경우 (상태 비 저장 프로그래밍을 원할 경우) 원래 객체를 기준으로 새로운 불변 ​​객체를 생성하는 데 적합하지 않습니다.


1

클론이 느립니다. 성능을 위해서는 아마도 빈 해시와 병합으로 시작해야합니다. 중첩 해시의 경우를 다루지 않습니다 ...

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

1

이것은 특별한 경우이지만 미리 정의 된 해시로 시작하여 복사하려는 경우 해시를 반환하는 메서드를 만들 수 있습니다.

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

h1 = johns

내가 가진 특정 시나리오는 일부 해시가 다른 해시를 빌드하는 JSON 스키마 해시 모음을 가지고 있다는 것입니다. 나는 처음에 그것들을 클래스 변수로 정의 하고이 사본 문제에 부딪쳤다.


0

아래를 사용하여 해시 객체를 딥 카피 할 수 있습니다.

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

16
이것은 Wayne Conrad의 답변과 중복됩니다.
Andrew Grimm

0

Ruby에는 백만 가지 방법이 있으므로 Enumerable을 사용하는 또 다른 방법이 있습니다.

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

-3

나를 위해 일한 Deep_Copy의 대체 방법.

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

h2는 h1의 참조가 아닌 h1의 배열 표현을 사용하여 형성되므로 deep_copy가 생성됩니다.


3
유망하게 들리지만 작동하지 않습니다. 이것은 또 다른 얕은 사본입니다
Ginty
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.