Ruby hash.delete(:key) также удаляет копии и клоны

Насколько я понимаю, когда вы устанавливаете объект = другому, это всегда будет ссылка, поэтому у нас есть такие методы, как .dup и .clone, чтобы фактически создать копию объекта, а не ссылку.

Однако я дублирую или клонирую массив хэшей, а затем, когда я удаляю ключ из исходного хеша, они удаляются из копий! Такого быть не должно, интересно, что я делаю не так.

Код:

or_data = {title: 'some title', tracks: [ { name: 'track one', position: 0, 
  artist: 'orignal artist', composer: 'original composer', duration: '1:30' }, 
  { name: 'track two', position: 1, artist: 'some other guy', 
  composer: 'beethoven', duration: '2:10' } ]  }

new_hash = or_data.dup
# or new_hash = or_data.clone, either way produces the same result

or_data[:tracks].each { |e| e.delete(:position) }

Ключ :position также будет удален из new_hash!

Это происходит независимо от того, использую ли я .dup или .clone.

Я только что прочитал сообщение, в котором говорится, что нужно использовать:

new_hash = Marshal.load( Marshal.dump(or_data) )

Это работает. Но почему? Потому что .dup и .clone делают «мелкие копии», что означает, что они создадут ссылку на :tracks (в этом примере) вместо копии, потому что это массив хэшей, содержащихся внутри хеша?


person kakubei    schedule 21.05.2013    source источник


Ответы (2)


Взгляните на код ниже:

or_data = {title: 'some title', tracks: [ { name: 'track one', position: 0, artist: 'orignal artist', composer: 'original composer', duration: '1:30' }, { name: 'track two', position: 1, artist: 'some other guy', composer: 'beethoven', duration: '2:10' } ]  }
new_hash = or_data.dup

p "Using .dup"
p "-----------"
p "or_data : #{or_data.object_id}"
p "new_hash : #{new_hash.object_id}"

p "or_data[:tracks] :#{or_data[:tracks].object_id}"
p "new_hash[:tracks] : #{new_hash[:tracks].object_id}"


or_data[:tracks].each { |e| e.delete(:position) }


new_hash = Marshal.load( Marshal.dump(or_data) )

p "Marshalling"
p "-----------"
p "or_data : #{or_data.object_id}"
p "new_hash : #{new_hash.object_id}"

p "or_data[:tracks] :#{or_data[:tracks].object_id}"
p "new_hash[:tracks] : #{new_hash[:tracks].object_id}"

Вывод:

"Using .dup"
"-----------"
"or_data : 5282580"
"new_hash : 5282568"
"or_data[:tracks] :5282592"
"new_hash[:tracks] : 5282592"

"Marshalling"
"-----------"
"or_data : 5282580"
"new_hash : 5282172"
"or_data[:tracks] :5282592"
"new_hash[:tracks] : 5282112"

Причина, по которой ключ position удаляется при использовании .dup или .clone, заключается в том, что ключ tracks по-прежнему относится к тому же объекту массива. Где, как и после маршалинга, ключ tracks относится ко всему новому объекту массива.

person Anand Shah    schedule 21.05.2013

Вы клонируете Hash object, а не Hash values. Он не выполняет вложенное клонирование.

В вашем случае массив ([ { name: 'track one', position: 0, artist: 'orignal artist', composer: 'original composer', duration: '1:30' }, { name: 'track two', position: 1, artist: 'some other guy', composer: 'beethoven', duration: '2:10' } ]) ссылается на один и тот же объект на clone или dup

person Rahul Tapali    schedule 21.05.2013