Cloning an array with its content

31,014

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)

Share:
31,014

Related videos on Youtube

Redouane Red
Author by

Redouane Red

Updated on February 11, 2021

Comments

  • Redouane Red
    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
      bkunzi01 almost 9 years
      They 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
    Redouane Red almost 9 years
    Thanks,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
    Horacio almost 9 years
    Nice!!! 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
    Nic Nilov almost 9 years
    In 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
    jazzytomato almost 9 years
    @RedouaneRed the string a[1] is the same object as b[1]. I'm not sure I understand your concern about symbols, because symbols are immutable
  • Redouane Red
    Redouane Red almost 9 years
    I 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
    Anutosh about 7 years
    The 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
    stema about 6 years
    You missed the trick here. You need to change one of the arrays and check if this change reflects to the other.
  • Sundeep
    Sundeep almost 6 years
    this is still not a deepcopy.. for ex: a = [[1, 2, [3, 4]]]; b = a.map(&:clone); a[0][2][0] = 'foo' will change b too
  • Sundeep
    Sundeep almost 6 years
    this also doesn't do deepcopy... try with a = [[1, 2, [3, 4]]]
  • wjordan
    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
    Sundeep almost 6 years
    yeah, 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
    Paul van Leeuwen almost 5 years
    What Sundeep said: if you do this replace approach instead of the clone 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
    Paul van Leeuwen almost 5 years
    Indeed, if you this map(&:clone) approach instead of the clone 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
    Paul van Leeuwen almost 5 years
    -1 For this question dup and clone 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 between dup and clone see stackoverflow.com/questions/10183370/…
  • ggorlen
    ggorlen almost 3 years
    Serializing 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 of a.each{|e| b << e.dup}.