Delete method also modifies the original variable

Suppose a code is like this:

array1 = [1,2,3,4,5]
array2 = array1
array2.delete(1)

And the result was:

array2 => [2,3,4,5]
array1 => [2,3,4,5]

How do I make sure the first array stays original after the modification
of second array?

Hi Vincent,

Try array1.dup, eg:

array2 = array1.dup

Cheers,
Garth

Vincent S. wrote in post #1105954:

How do I make sure the first array stays original after the modification
of second array?

Variables in ruby hold references to objects. This can be evidenced by
viewing the object_ids:

irb:001> array1 = [1,2,3,4,5]
irb:002> array2 = array1
irb:003> array2.object_id
=> 70171269757120
irb:004> array1.object_id
=> 70171269757120

To de-duplicate the reference you need to create a second object, which
is a copy of the first. The simplest way is to use dup

irb:005> array2 = array1.dup
irb:006> array1.object_id
=> 70171269757120
irb:007> array2.object_id
=> 70171269775520

^^^^^

irb:008> array1.delete(1)
irb:009> array1
=> [2, 3, 4, 5]
irb:010> array2
=> [1, 2, 3, 4, 5]

The thing to remember is that Array#dup performs a shallow copy. For
example:

irb:011> array1 = [[1,2,3],[4,5]]
irb:012> array2 = array1.dup
irb:013> array1[0].delete(1)
irb:014> array1
=> [[2, 3], [4, 5]]
irb:015> array2
=> [[2, 3], [4, 5]]

The variables array1 and array2 point to different Array objects,
but each of those independently maintains references to the same
inner-arrays. In other words, array1[0] and array2[0] both reference
the same object.

It’s not only limited to arrays, either:

irb:016> array1 = [‘a’]
irb:017> array2 = array1.dup
irb:018> array1[0] << ‘b’
irb:019> array1
=> [“ab”]
irb:020> array2
=> [“ab”]

There is no general-form solution to creating a deep-copy of a
datastructure in ruby.

On Tue, Apr 16, 2013 at 9:10 PM, Matthew K.
[email protected]wrote:

There is no general-form solution to creating a deep-copy of a
datastructure in ruby.

A (suboptimal) solution for deep copies in Ruby is to use Marshal.

It sure would be nice if Ruby had some sort of analogue for structured
cloning.

Thanks much for help! :slight_smile:

On Wed, Apr 17, 2013 at 7:34 AM, Tony A.
[email protected]wrote:

What exactly do you mean by “structured cloning”? Do you have ideas how
that should work?

Sketch:

untested

class CloneContext
def initialize
@store = {}
end

def deep_clone(*objs)
objs.map do |o|
copy_one(o) rescue o
end
end

private
def copy_one(obj)
@store.fetch obj.object_id do |oid|
(@store[oid] = obj.class.allocate).tap do |copy|
# add specific handling for Array, String etc. or delegate to
#clone method of obj
obj.instance_variables.each do |var|

copy.instance_variable_set(deep_clone(obj.instance_variable_get(var)))
end
end
end
end
end

Kind regards

robert

On Wed, Apr 17, 2013 at 5:51 AM, Vincent S.
[email protected]wrote:

How do I make sure the first array stays original after the modification
of second array?

Adding to what Garthy said: you need to be aware that there is only one
Array, not two as your speaking of a “first array” and “second array”
indicates. The effect is called “aliasing”: two variables reference the
same instance. Btw, it happens all the time, for example when invoking
methods.

Another way of preventing changes to an Array is freezing it. That does
not automatically freeze the contained objects though!

Yet another way is to do the filtering in one step

irb(main):001:0> array1 = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
irb(main):002:0> array2 = array1.reject {|x| x == 1}
=> [2, 3, 4, 5]
irb(main):003:0> array2 = array1[1…-1]
=> [2, 3, 4, 5]
irb(main):004:0> array2 = array1.slice(1…-1)
=> [2, 3, 4, 5]
irb(main):005:0> array1
=> [1, 2, 3, 4, 5]

Kind regards

robert

On Wed, Apr 17, 2013 at 3:00 AM, Robert K.
[email protected]wrote:

What exactly do you mean by “structured cloning”? Do you have ideas how
that should work?

You can think of it as being somewhat akin to the marshal/unmarshal
approach, minus the intermediate string representation. For more
information I’d suggest reading how ECMAScript does it: