Как клонировать массив хэшей и добавить значение ключа, используя каждый цикл - PullRequest
0 голосов
/ 03 февраля 2020

Я хочу клонировать массив хэшей, а затем клонировать его в несколько.

irb(main):001:0> arr = [{a: "one", b: "two"}, {a: "uno", b: "due"}, {a: "en", b: "to"}]
=> [{:a=>"one", :b=>"two"}, {:a=>"uno", :b=>"due"}, {:a=>"en", :b=>"to"}]
irb(main):002:0> arr_1 = arr.clone
=> [{:a=>"one", :b=>"two"}, {:a=>"uno", :b=>"due"}, {:a=>"en", :b=>"to"}]
irb(main):003:0> arr_2 = arr.clone
=> [{:a=>"one", :b=>"two"}, {:a=>"uno", :b=>"due"}, {:a=>"en", :b=>"to"}]

Динамически я хочу добавить id в хэши.

irb(main):004:0> arr_1.each { |k| k[:id] = 1 }
=> [{:a=>"one", :b=>"two", :id=>1}, {:a=>"uno", :b=>"due", :id=>1}, {:a=>"en", :b=>"to", :id=>1}]
irb(main):005:0> arr_2.each { |k| k[:id] = 2 }
=> [{:a=>"one", :b=>"two", :id=>2}, {:a=>"uno", :b=>"due", :id=>2}, {:a=>"en", :b=>"to", :id=>2}]

Но на результат arr_1 id влияет arr_2 каждый л oop операция, которая становится 2

irb(main):006:0> arr_1
=> [{:a=>"one", :b=>"two", :id=>2}, {:a=>"uno", :b=>"due", :id=>2}, {:a=>"en", :b=>"to", :id=>2}]

Я пытался использовать

arr_1 = arr
arr_2 = arr

, но результат продолжает показывать тот же результат.
Как сделать arr_1 хэши :id = 1 и arr_2 хэши :id = 2?

1 Ответ

2 голосов
/ 03 февраля 2020

Посмотрим, что происходит.

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.

...