Как мне скопировать хеш в Ruby? - PullRequest
190 голосов
/ 11 ноября 2010

Я признаю, что я немного новичок в рубине (сейчас пишу сценарии рейка).На большинстве языков легко найти конструкторы копирования.Полчаса поиска не нашел его в рубине.Я хочу создать копию хэша, чтобы я мог изменить ее, не затрагивая исходный экземпляр.

Некоторые ожидаемые методы, которые не работают должным образом:

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

Ответы [ 13 ]

212 голосов
/ 11 ноября 2010

Метод 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 класса.

173 голосов
/ 11 ноября 2010

Как уже отмечали другие, 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 - это имя Руби для сериализация .При маршаллинге объект - вместе с объектами, на которые он ссылается - преобразуется в серию байтов;эти байты затем используются для создания другого объекта, такого как оригинал.

68 голосов
/ 29 июня 2013

Если вы используете Rails, вы можете сделать:

h1 = h0.deep_dup

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

13 голосов
/ 21 января 2012

Хеш может создать новый хеш из существующего хеша:

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
5 голосов
/ 29 мая 2015

Я также новичок в Ruby и столкнулся с похожими проблемами при дублировании хэша. Используйте следующее. Я не имею понятия о скорости этого метода.

copy_of_original_hash = Hash.new.merge(original_hash)
3 голосов
/ 15 ноября 2015

Как упомянуто в разделе «Вопросы безопасности» документации маршала ,

Если вам нужно десериализовать ненадежные данные, используйте JSON или другое Формат сериализации, который может загружать только простые, «примитивные» такие типы, как String, Array, Hash и т. д.

Вот пример того, как выполнить клонирование с использованием JSON в 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"}
1 голос
/ 18 июня 2013

Клон медленный.Для производительности, вероятно, следует начинать с пустого хэша и слияния.Не распространяется на случай вложенных хэшей ...

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)
  
1 голос
/ 26 февраля 2013

Поскольку стандартный метод клонирования сохраняет замороженное состояние, он не подходит для создания новых неизменяемых объектов на основе исходного объекта, если вы хотите, чтобы новые объекты немного отличались от исходного (если вам нравится программирование без сохранения состояния).

1 голос
/ 28 июля 2011

вы можете использовать ниже для глубокого копирования объектов Hash.

deeply_copied_hash = Marshal.load(Marshal.dump(original_hash))
1 голос
/ 11 ноября 2010

Использование Object#clone:

h1 = h0.clone

(В документации clone содержится путаница, что initialize_copy - это способ переопределить это, но ссылка для этого методав Hash направляет вас на replace вместо ...)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...