web-dev-qa-db-ja.com

Rubyでハッシュをコピーするにはどうすればよいですか?

私はRuby初心者です(今はrakeスクリプトを書いています)ことを認めます。ほとんどの言語では、コピーコンストラクターは簡単に見つかります。 30分間の検索では、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
190
Precipitous

clone メソッドは、Rubyの標準である shallow-copy を実行する組み込みの方法です。

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メソッドで文書化されます。

212
Mark Rushakoff

他の人が指摘したように、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は、マーシャリングできるすべてのオブジェクトに対して機能します。ほとんどの組み込みデータ型(配列、ハッシュ、文字列、&c)はマーシャリングできます。

マーシャリングは、Rubyの serialization の名前です。マーシャリングでは、オブジェクトは、それが参照するオブジェクトとともに、一連のバイトに変換されます。これらのバイトは、元のような別のオブジェクトを作成するために使用されます。

173
Wayne Conrad

Railsを使用している場合、次のことができます。

h1 = h0.deep_dup

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

68
lmanners

ハッシュは、既存のハッシュから新しいハッシュを作成できます。

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
13
James Moore

私はRubyの初心者でもあり、ハッシュの複製で同様の問題に直面しました。以下を使用してください。この方法の速度についてはわかりません。

copy_of_original_hash = Hash.new.merge(original_hash)
5
Kapil Aggarwal

Marshalドキュメントのセキュリティに関する考慮事項セクション で述べたように、

信頼できないデータを逆シリアル化する必要がある場合は、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"}
3
Wand Maker

以下を使用して、ハッシュオブジェクトをディープコピーできます。

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

標準のクローン作成方法は凍結状態を保持するため、元のオブジェクトに基づいて新しい不変オブジェクトを作成するのには適していません(新しいオブジェクトを元のオブジェクトとわずかに異なるようにする場合(ステートレスプログラミングが必要な場合))。

1
kuonirat

クローンが遅い。パフォーマンスのために、おそらく空白のハッシュとマージで開始する必要があります。ネストされたハッシュの場合はカバーしません...

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
 
ベンチユーザーシステムの合計(実数)
 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)
 
1
Justin

Object#clone を使用します。

h1 = h0.clone

(紛らわしいことに、cloneのドキュメントには、initialize_copyがこれをオーバーライドする方法であると書かれていますが、Hashのそのメソッドへのリンクは、代わりにreplaceに移動します...)

1
Josh Lee

これは特別な場合ですが、取得してコピーを作成する定義済みのハッシュで開始する場合は、ハッシュを返すメソッドを作成できます。

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

h1 = johns

私が持っていた特定のシナリオは、いくつかのハッシュが他のハッシュを構築するJSONスキーマハッシュのコレクションを持っていたことでした。最初にそれらをクラス変数として定義していましたが、このコピーの問題に遭遇しました。

0
grumpasaurus

Rubyには百万通りの方法があるため、Enumerableを使用する別の方法を次に示します。

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