Посмотрим, что происходит.
arr = [{a: "cat", b: "dog"}, {a: "uno", b: "due"}]
arr.object_id
#=> 4557280
arr1 = arr
arr1.object_id
#=> 4557280
Как видите, переменные arr
и arr1
содержат один и тот же объект потому что объекты имеют одинаковый идентификатор объекта. 1 Поэтому, если этот объект изменен, arr
и arr1
все равно оба будут содержать этот объект. Давайте попробуем.
arr[0] = {a: "cat", b: "dog"}
arr
#=> [{:a=>"cat", :b=>"dog"}, {:a=>"uno", :b=>"due"}]
arr.object_id
#=> 4557280
arr1
#=> [{:a=>"cat", :b=>"dog"}, {:a=>"uno", :b=>"due"}]
arr1.object_id
#=> 4557280
Если мы хотим иметь возможность изменять arr
таким образом, чтобы это не влияло на arr1
, мы используйте метод Ядро # dup .
arr
#=> [{:a=>"cat", :b=>"dog"}, {:a=>"uno", :b=>"due"}]
arr1 = arr.dup
#=> [{:a=>"cat", :b=>"dog"}, {:a=>"uno", :b=>"due"}]
arr.object_id
#=> 4557280
arr1.object_id
#=> 3693480
arr.map(&:object_id)
#=> [2631980, 4557300]
arr1.map(&:object_id)
#=> [2631980, 4557300]
Как видите, arr
и arr1
теперь содержат разные объекты. Однако эти объекты являются массивами, соответствующие элементы (хэши) которых являются одинаковыми объектами. Давайте изменим один из элементов arr
.
arr[1][:a] = "owl"
arr
#=> [{:a=>"cat", :b=>"dog"}, {:a=>"owl", :b=>"due"}]
arr.map(&:object_id)
#=> [2631980, 4557300]
arr
по-прежнему содержит те же объекты, но мы изменили один. Давайте посмотрим на arr1
.
arr1
#=> [{:a=>"cat", :b=>"dog"}, {:a=>"owl", :b=>"due"}]
arr1.map(&:object_id)
#=> [2631980, 4557300]
Должны ли мы удивляться, что arr1
также изменился?
Нам нужно dup
и arr
, и элементы arr
.
arr = [{a: "one", b: "two"}, {a: "uno", b: "due"}]
arr1 = arr.dup.map(&:dup)
#=> [{:a=>"one", :b=>"two"}, {:a=>"uno", :b=>"due"}]
arr.object_id
#=> 4149120
arr1.object_id
#=> 4182360
arr.map(&:object_id)
#=> [4149200, 4149140]
arr1.map(&:object_id)
#=> [4182340, 4182280]
Сейчас arr
и arr1
это разные объекты, и они содержат разные (ха sh) объекты, поэтому любое изменение одного не повлияет на другое. (Попробуйте.)
Теперь предположим, что arr
были следующими:
arr = [{a: "cat", b: [1,2]}]
Давайте сделаем копию.
arr1 = arr.dup.map(&:dup)
#=> [{:a=>"cat", :b=>[1, 2]}]
Теперь изменим arr[0][:b]
.
arr[0][:b] << 3
#=> [{:a=>"cat", :b=>[1, 2, 3]}]
arr1
#=> [{:a=>"cat", :b=>[1, 2, 3]}]
Драт! arr1
изменено. Мы можем снова посмотреть на идентификаторы объектов, чтобы понять, почему это произошло.
arr.object_id
#=> 4488500
arr1.object_id
#=> 4503140
arr.map(&:object_id)
#=> [4488520]
arr1.map(&:object_id)
#=> [4503100]
arr[0][:b].object_id
#=> 4488560
arr1[0][:b].object_id
#=> 4488560
Мы видим, что arr
и arr1
- это разные объекты, и соответствующие хэши - это одни и те же элементы, но массив является одним и тем же объектом для обоих хешей. Поэтому нам нужно сделать что-то вроде этого:
arr1[0][:b] = arr[0][:b].dup
, но этого все равно недостаточно, если бы arr
было:
arr = [{a: "cat", b: [1,[2,3]]}]
Нам нужен метод, который сделает глубокая копия . Распространенным решением для этого является использование методов Marshal :: dump и Marshal :: load .
arr = [{a: "cat", b: [1,2]}]
str = Marshal.dump(arr)
#=> "\x04\b[\x06{\a:\x06aI\"\bcat\x06:\x06ET:\x06b[\ai\x06i\a"
arr1 = Marshal.load(str)
#=> [{:a=>"cat", :b=>[1, 2]}]
arr[0][:b] << 3
#=> [{:a=>"cat", :b=>[1, 2, 3]}]
arr
#=> [{:a=>"cat", :b=>[1, 2, 3]}]
arr1
#=> [{:a=>"cat", :b=>[1, 2]}]
Обратите внимание, что мы могли бы написать:
arr1 = Marshal.load(Marshal.dump(arr))
Как объяснено в do c, сериализация, используемая методами Marshal
, не обязательно одинакова для разных версий Ruby. Если, например, dump
использовалось для создания строки, которая была сохранена в файл, а позже load
было вызвано для содержимого файла, с использованием другой версии Ruby, содержимое может быть недоступно для чтения. Конечно, это не проблема в этом применении методов.
1. Чтобы было легче увидеть различия в идентификаторах объектов, я показал только последние семь цифр. Им во всех случаях предшествуют цифры 4877798
.