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 все още препраща към същия обект от масив. Където след Marshalling ключът 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