0

I have an array 'a' and I later set a new array 'b' equal to 'a'. When I remove an element from either array, they both change rather than the explicitly referenced array. I think this is because they're pointing to the same underlying object?

a= ["cat","hat","dog"]
b= a
a.delete_at(0)
print a
>> a = ["hat","dog"
print b
>> b = ["hat","dog"]

How can I set two variables equal but allow them to be independent for later operations? I'm looking for behaviour like this:

a= ["cat","hat","dog"]
b= a
a.delete_at(0)
print a
>> a = ["hat","dog"
print b
>> b = ["cat","hat","dog"]
0

2 Answers 2

3

Use Object#dup to create a duplicate of the original array:

a = ["cat","hat","dog"]
b = a.dup # b now points to a duplicate of a, not a itself

b # => ["cat","hat","dog"]
b.delete_at(0)

b # => ["hat","dog"]
a # => ["cat","hat","dog"]

To verify this behavior:

a = [1, 2, 3]
b = a
a.object_id == b.object_id # => true, a and b point to the same objects

a = [1, 2, 3]
b = a.dup
a.object_id == b.object_id # => false, a and b point to different objects
Sign up to request clarification or add additional context in comments.

1 Comment

I just noticed that the "DUP" method doesn't work when doing string manipulations. For example, if I do b[0].upcase! the first element of both arrays is changed. Any insight into that?
0

It is perhaps worth noting that if

a = [["cat", "pig"], "hat", "dog"] # => [["cat", "pig"], "hat", "dog"]
b = a.dup                          # => [["cat", "pig"], "hat", "dog"]

then

a[0].delete_at(0)
a                                  # => [["pig"], "hat", "dog"] 
b                                  # => [["pig"], "hat", "dog"]

Here you'd need to do this:

a = [["cat", "pig"], "hat", "dog"]
b = a.dup 
b[0] = a[0].dup
a[0].delete_at(0)
a                                  # => [["pig"], "hat", "dog"] 
b                                  # => [["cat", "pig"], "hat", "dog"] 

Now suppose a were much more complicated, say, an array of hashes whose values are arrays of hashes. Obviously, one must take care in making a copy of a that will be unaffected by the deletion of some deeply-nested element of a. What you would want is called a "deep copy", as opposed to the "shallow" copy made by Object#dup and Object#clone.

Fortunately, there is an easy way to make a deep-copy of any object, using Marshal#dump and Marshal#load:

b = Marshal.load(Marshal.dump(a))
a[0].delete_at(0)
a                                  #  => [["pig"], "hat", "dog"]
b                                  #  => [["cat", "pig"], "hat", "dog"]

As an aside, Marshal is also used to save an object to file for later retrieval:

File.open('myfile', 'w+') do |f|  
  Marshal.dump(a, f)  
end  

File.open('myfile') do |f|  
  c = Marshal.load(f)  
end  
c                                  #  => [["cat", "pig"], "hat", "dog"]

A word of caution: Marshal files are not not portable among all versions of Ruby.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.