Cloning an array with its content
Solution 1
You need to do a deep copy of your array.
Here is the way to do it
Marshal.load(Marshal.dump(a))
This is because you are cloning the array but not the elements inside. So the array object is different but the elements it contains are the same instances. You could, for example, also do a.each{|e| b << e.dup}
for your case
Solution 2
Instead of calling clone
on the array itself, you can call it on each of the array's elements using map
:
b = a.map(&:clone)
This works in the example stated in the question, because you get a new instance for each element in the array.
Solution 3
You can use #dup which creates a shallow copy of the object, meaning "the instance variables of object are copied, but not the objects they reference." For instance:
a = [1, 2, 3]
b = a.dup
b # => [1, 2, 3]
Source: https://ruby-doc.org/core-2.5.3/Object.html#method-i-dup
Edit: Listen to Paul below me. I misunderstood the question.
Solution 4
Try this:
b = [] #create a new array
b.replace(a) #replace the content of array b with the content from array a
At this point, these two arrays are references to different objects and content are the same.
Solution 5
You can just map the elements of the array. I believe this is the cleanest solution as of today.
array.map(&:itself)
Related videos on Youtube
Redouane Red
Updated on February 11, 2021Comments
-
Redouane Red about 3 years
I want to make a copy of an array, to modify the copy in-place, without affecting the original one. This code fails
a = [ '462664', '669722', '297288', '796928', '584497', '357431' ] b = a.clone b.object_id == a.object_id # => false a[1][2] = 'X' a[1] #66X722 b[1] #66X722
The copy should be different than the object. Why does it act like if it were just a reference?
-
bkunzi01 almost 9 yearsThey are two different objects if you use the inspect method it should show different values for memory allocated. Cloning copies the variables but not the objects they reference.
-
-
Redouane Red almost 9 yearsThanks,I'll use that,but I thought that only symbols reference the same object,why do the strings a[1] and b[1] reference the same object?
-
Horacio almost 9 yearsNice!!! I've tried with dup, clone and Array.new(a) and still change it, I think that ruby creates an array with pointers to each values.
-
Nic Nilov almost 9 yearsIn Ruby everything is an object, so the array contains references to the string and not the strings themselves. When you clone the array, references get copied, but the copies keep pointing to the original strings.
-
jazzytomato almost 9 years@RedouaneRed the string
a[1]
is the same object asb[1]
. I'm not sure I understand your concern about symbols, because symbols are immutable -
Redouane Red almost 9 yearsI just meant that when you create 2 similar strings,you create 2 objects,but when you create 2 similar symbols,you only create 1,thanks @NicNilov for the explaination.
-
Anutosh about 7 yearsThe solution works. Not sure if someone has already answered this , becaz, it may have escaped my search efforts, since couldn't find the solution after searching, Don't want to repeat if already answered. Hope this helps.
-
stema about 6 yearsYou missed the trick here. You need to change one of the arrays and check if this change reflects to the other.
-
Sundeep almost 6 yearsthis is still not a deepcopy.. for ex:
a = [[1, 2, [3, 4]]]; b = a.map(&:clone); a[0][2][0] = 'foo'
will changeb
too -
Sundeep almost 6 yearsthis also doesn't do deepcopy... try with
a = [[1, 2, [3, 4]]]
-
wjordan almost 6 years@Sundeep this question is about a simple array of objects, so a deep copy is not necessary. You can post your new example as a separate question to Stack Overflow if you would like it discussed in more detail.
-
Sundeep almost 6 yearsyeah, for the given example in question, this works.. I should've mentioned my comment as fyi/note.. did not mean to say answer is wrong..
-
Paul van Leeuwen almost 5 yearsWhat Sundeep said: if you do this
replace
approach instead of theclone
approach as posted in the question you will get the exact same result. So this is just a different way to the same thing as the OP did (while he was clearly looking for a different result). -
Paul van Leeuwen almost 5 yearsIndeed, if you this
map(&:clone)
approach instead of theclone
approach as posted in the question, this will give the result that the OP was looking for. So indeed a valid answer. -
Paul van Leeuwen almost 5 years-1 For this question
dup
andclone
yield the exact same result as the OP got, so not really an answer to this question. For those who are now curious about the difference betweendup
andclone
see stackoverflow.com/questions/10183370/… -
ggorlen almost 3 yearsSerializing and deserializing isn't necessary here and isn't useful when the objects in the array can't be stringified. I suspect this has extra overhead. This answer (
a.map(&:clone)
) is the best solution to OP's specific problem, a more idiomatic version ofa.each{|e| b << e.dup}
.