Как скопировать хэш в 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
Ответы
Ответ 1
Метод 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
для класса.
Ответ 2
Как указывали другие, 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 и am. C.) Могут быть объединены.
Маршаллинг - это имя Ruby для serialization. При сортировке объект - с объектами, на которые он ссылается - преобразуется в ряд байтов; эти байты затем используются для создания другого объекта, такого как оригинал.
Ответ 3
Если вы используете Rails, вы можете сделать:
h1 = h0.deep_dup
http://apidock.com/rails/Hash/deep_dup
Ответ 4
Хэш может создать новый хэш из существующего хэша:
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
Я тоже новичок в Ruby, и я столкнулся с подобными проблемами при дублировании хэша. Используйте следующее. Я не знаю о скорости этого метода.
copy_of_original_hash = Hash.new.merge(original_hash)
Ответ 6
Используйте Object#clone
:
h1 = h0.clone
(Вряд ли, документация для clone
говорит, что initialize_copy
- это способ переопределить это, но ссылка для этого метода в Hash
направляет вас на replace
вместо этого...)
Ответ 7
вы можете использовать ниже для глубокого копирования объектов Hash.
deeply_copied_hash = Marshal.load(Marshal.dump(original_hash))
Ответ 8
Как упоминалось в разделе "Вопросы безопасности" документации Marshal,
Если вам нужно десериализовать недоверенные данные, используйте 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"}
Ответ 9
Это особый случай, но если вы начинаете с предопределенного хэша, который хотите захватить и сделать копию, вы можете создать метод, который возвращает хэш:
def johns
{ "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"}
end
h1 = johns
Конкретный сценарий, который у меня был, состоял в том, что у меня была коллекция хэшей JSON-схемы, в которых некоторые хеши строились на других. Первоначально я определял их как переменные класса и сталкивался с этой проблемой.
Ответ 10
Поскольку у Ruby есть миллион способов сделать это, здесь другой способ использования Enumerable:
h0 = { "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"}
h1 = h0.inject({}) do |new, (name, value)|
new[name] = value;
new
end
Ответ 11
Поскольку стандартный метод клонирования сохраняет замороженное состояние, он не подходит для создания новых неизменяемых объектов на основе исходного объекта, если вы хотите, чтобы новые объекты немного отличались от оригинала (если вам нравится программирование без состояния).
Ответ 12
Клон медленный. Для производительности, вероятно, следует начинать с чистого хеша и слияния. Не распространяется на случаи вложенных хешей...
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)
Ответ 13
Альтернативный способ Deep_Copy, который работал у меня.
h1 = {:a => 'foo'}
h2 = Hash[h1.to_a]
Это создало deep_copy, поскольку h2 формируется с использованием представления массива h1, а не h1-ссылок.