Change value of a cloned object - ruby

I have an object that has an instance variable array called my_array. I declare it via attr_accessor.
my_object.my_array = [1,2,3] # <= I don't know the max size of my_array(it can be dynamic)
I want to create the same object with my_object and fill its my_array with only one element. The value inside that element is each value from my_array's element (from my_object). Because the size of my_array is dynamic I suppose I need to iterate it via each.
Here's my attempt:
my_object.my_array.each do |element| # <= my_object is a special object
new_object = nil unless new_object.nil?
new_object = my_object.clone # <= I create new object with same class with my_object
new_object.my_array.clear # <= clear all element inside it.
new_object.my_array.push(element) # assign element value to the first element.
# rest of code #
new_object = nil
end
The iteration is not iterating properly. The size of my_object.my_array is 3, then it should iterating three times, but it's not, it's only iterating one time. I believe it's because of new_object.my_array.clear, but I cloned it from my_object, so why this happened?

When you assign one array it just copies the reference and both of them point to the same reference,
so a change in one is reflected when you print either of them:
orig_array = [1,2,3,4]<br>
another_array = orig_array
puts orig_array.unshift(0).inspect
puts another_array.inspect
Which outputs:
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4]
To avoid this you can use Marshal to copy from the original array without impacting the object to which it is copied.
Any changes in the original array will not change the object to which it is copied.
orig_array = [1,2,3,4]
another_array = Marshal.load(Marshal.dump(orig_array))
puts orig_array.unshift(0).inspect
puts another_array.inspect
Which outputs:
[0, 1, 2, 3, 4]
[1, 2, 3, 4]

The problem is, that clone will make a shallow clone, not a deep clone. In other words, my_array is a reference and the cloned instance references the same array in memory. Consider:
class MyClass
attr_accessor :my_array
end
a = MyClass.new
a.my_array = [1, 2, 3]
a.my_array
#=> [1, 2, 3]
b = a.clone
b.my_array.push(4)
b.my_array
#=> [1, 2, 3, 4]
a.my_array # also changed!
#=> [1, 2, 3, 4]
In order to fix this, you need to extend the initialize_copy method to also clone the array:
class MyClass
attr_accessor :my_array
def initialize_copy(orig)
super
self.my_array = orig.my_array.clone
end
end
a = MyClass.new
a.my_array = [1, 2, 3]
a.my_array
#=> [1, 2, 3]
b = a.clone
b.my_array.push(4)
b.my_array
#=> [1, 2, 3, 4]
a.my_array # did not change, works as expected
#=> [1, 2, 3]

Related

What is a good way to access instances?

How do I write my class to access array for different objects?
class ListArray
attr_accessor :arr
def initialize arr
#arr = arr
end
end
a = ListArray.new([0, 1, 2, 3])
b = ListArray.new(a.arr)
a.arr[2] = 999
b.arr[2] = 4
a.arr[2] #=> 4 ?
b.arr[2] #=> 4
When I changed value of b.arr[2] to 4, a.arr[2] value (which should be 999) takes the value 4.
I don't know what I did wrong.
[edit]
My full code looks more like this:
class OtherClass
def list
end
class ListArray
attr_accessor :arr
def initialize arr
#arr = arr
end
def putItem ...
def getItem ...
def cutList &bloc ...
end
a = ListArray.new obj1_other_class.list
# obj2_other_class.list => [[1, 2], [3, 4], ... ]
# [3, 4] is an item
b = ListArray.new obj2_other_class.list
a.putItem [5, 6]
c = ListArray.new a.arr
c.arr += b.arr
c.arr[1][0] = 7
...
How can I avoid object's same id problems?
class ListArray
attr_accessor :arr
def initialize(arr)
#arr = arr
end
def arr_object_id
#arr.object_id
end
end
Create an instance of ListArray, creating an instance variable #arr equal to [0, 1, 2, 3]:
a = ListArray.new [0, 1, 2, 3]
#=> #<ListArray:0x0000574d960e19e8 #arr=[0, 1, 2, 3]>
Let's check the value of #arr and retrieve its object id:
a.arr
#=> [0, 1, 2, 3]
a.arr_object_id
#=> 47995370802440
Now create another instance of ListArray, creating its instance variable #arr and setting it equal to the value of a.arr:
b = ListArray.new(a.arr)
#=> #<ListArray:0x0000574d9611bdf0 #arr=[0, 1, 2, 3]>
b.arr
#=> [0, 1, 2, 3]
b.arr_object_id
#=> 47995370802440
The interesting thing here is that a.arr_object_id == b.arr_object_id. That's not surprising, however, because we initialized b's instance variable to a's instance variable, so they are the same object!
Next, change the value of a's instance variable to [0, 1, 999, 3]:
a.arr[2] = 999
a.arr
#=> [0, 1, 999, 3]
a.arr_object_id
#=> 47995370802420
Check if the value of b's instance variable has changed:
b.arr
#=> [0, 1, 999, 3]
b.arr_object_id
#=> 47995370802440
It has, because a's and b's instance variables #arr hold the same object.
To make b's instance variable hold an array whose instance variables are the same as a's, but make the two arrays different objects, create b with its instance variable #arr equal to a copy of the value of a's instance variable:
a = ListArray.new [0, 1, 2, 3]
#=> #<ListArray:0x0000574d9610d818 #arr=[0, 1, 2, 3]>
a.arr_object_id
#=> ...320
b = ListArray.new(a.arr.dup)
#=> #<ListArray:0x0000574d961143c0 #arr=[0, 1, 2, 3]>
b.arr
#=> [0, 1, 2, 3]
b.arr_object_id
#=> ...100 (different than a.arr_object_id)
a.arr[2] = 19
a.arr
#=> [0, 1, 19, 3]
b.arr
#=> [0, 1, 2, 3]
But, wait, we're not finished. Here's a second example that illustrates why you cannot always merely apply dup.
a = ListArray.new [0, [1, 2], 3]
#=> #<ListArray:0x0000574d9614b370 #arr=[0, [1, 2], 3]>
a.arr_object_id
#=> ...700
a.arr[1].object_id
#=> ...720
a.arr[1][1].object_id
#=> 5
2.object_id
#=> 5
b = ListArray.new(a.arr.dup)
#=> #<ListArray:0x0000574d96119258 #arr=[0, [1, 2], 3]>
b.arr
#=> [0, [1, 2], 3]
b.arr_object_id
#=> ...160 (different than a.arr_object_id)
b.arr[1].object_id
#=> ...720 (same as a.arr[1].object_id)
b.arr[1][1].object_id
#=> 5
Now change the value of a.arr[1][1]:
a.arr[1][1] = 9
a.arr
#=> [0, [1, 9], 3] (as expected)
a.arr[1].object_id
#=> ...720 (no change)
b.arr
#=> [0, [1, 9], 3]
b.arr[1].object_id
#=> ...720 (no change)
You see this changes b[1][1] as well. That's because the contents of the object that is the value of both a.arr[1] and b.arr[1] has been altered. Now try this.
a.arr[1] = [8, 0]
a.arr
#=> [0, [8, 0], 3] (as expected)
a.arr[1].object_id
#=> ...880 (a new object!)
b.arr
#=> [0, [1, 9], 3] (unchanged!)
b.arr[1].object_id
#=> ...720 (unchanged)
For this example we would need to write:
a = ListArray.new [0, [1, 2], 3]
b = ListArray.new(a.arr.dup.map { |e| e.dup })
a.arr[1][1] = 9
a.arr
#=> [0, [1, 9], 3]
b.arr
#=> [0, [1, 2], 3] (no change!)
a.arr.dup.map { |e| e.dup } is referred to as a deeper copy of a.arr than is a.arr.dup. If there were yet deeper nested arrays ([1, [2, [3, 4]], 5]) we would have to dup to lower levels of a.arr. For a Ruby newbie it's not important to fully understand how deep copies are constructed, merely that they are needed to achieve independence for duped copies of objects.
By
b = ListArray.new a.arr
You actually transfer the reference of a.arr to b, not it's value.
You can do it like this:
b = ListArray.new a.arr.dup
Check this question, which is about the argument passing of reference or value.

How to use a recursive array

I have array, named a and define it with [1, 2, 3].
Next, I pushed it to itself:
a = [1, 2, 3]
a << a
and the result I get is:
#=> [1, 2, 3, [...]]
When I want to get the last element of array using a.last I get:
a.last
#=> [1, 2, 3, [...]]
#even
a.last.last.last
#=> [1, 2, 3, [...]]
What is going on, when we would push array to itself?
Yes, I understand that this should create a recursive array, but what can we do with it?
In Ruby variables, array elements etc. are object references. So when you do a = [1, 2, 3], there will be an array somewhere in memory and the a variable is a reference to that memory. Now when you do a << a, a[4] will also be a reference to that object. So in effect a now contains a reference to itself.
a = [1, 2, 3]
a << a.dup
a.last
=> [1, 2, 3]
a.last.last
=> 3
Maybe this is what you wanted. This just insert an array [1, 2, 3] as the last item of the a array. In the way you did you put a reference at the end of the a array and this becomes recursive.

New array declaration in Ruby

In Ruby, what is the difference between:
new_array = old_array
and
new_array = Array.new(old_array)
The first one is assignment, and will not create a new object. The second one is invalid (under default definition of Object), and will raise an argument error.
Consider the following:
old_arr = [1,2,3] #=> [1, 2, 3]
old_arr.object_id #=> 70142672048160
new_arr = old_arr #=> [1, 2, 3]
new_arr.object_id #=> 70142672048160
newer_arr = Array.new(old_arr) #=> [1, 2, 3]
newer_arr.object_id #=> 70142675883800
So you see the local variables old_arr and new_arr hold the same array object. By contrast, the variable newer_arr holds a different array object.
Now try this:
old_arr[0] = 'X' #=> "X"
old_arr #=> ["X", 2, 3]
then
new_arr #=> ["X", 2, 3]
newer_arr #=> [1, 2, 3]
newer_arr points to what is called a "shallow copy" of old_arr. To see what this means, consider another example:
old_arr = [1,2,[3,4]] #=> [1, 2, [3, 4]]
new_arr = old_arr #=> [1, 2, [3, 4]]
newer_arr = Array.new(old_arr) #=> [1, 2, [3, 4]]
old_arr[0] = 'X' #=> "X"
old_arr[2][0] = 'Y' #=> "Y"
old_arr #=> ["X", 2, ["Y", 4]]
new_arr #=> ["X", 2, ["Y", 4]]
newer_arr #=> [1, 2, ["Y", 4]]
As before, newer_arr[0] has not been modified, and newer_arr[2] contains the same object as it did previously, but that object has been modified.
"what happens in case we use an Array or String instead of Object?"
That depends on the class definition itself, so you should look at the documentation.
Basically, #new method creates a new object and then calls "initialize" method with parameters passed to it.
To answer your question:
Array.new method copies the array passed to it (actually, as i said before: it passes that array to initialize method, which copies it).
Same happens with String.
The easiest way to know such things is actually repl.
In irb execute following lines, one by one:
a = ":)"
b = a
a[1] = '('
puts a
puts b
a = ":)"
b = String.new(a)
a[1] = '('
puts a
puts b
a = [1,2,3]
b = a
a[1] = 10
puts a
puts b
a = [1,2,3]
b = Array.new(a)
a[1] = 10
puts a
puts b

Creating new array by inserting a new element to an existing array

I would like to create a new array by inserting a new element to an existing array. But I do not want to mutate the existing array. I want:
A = [1, 2, 3]
to remains as is, while creating:
B = [1, 2, 3, 4]
Any suggestions?
This code:
B = A << 4
results in:
B = [1, 2, 3, 4]
A = [1, 2, 3, 4]
Do
B = A + [4]
or
B = [*A, 4]
or
B = A.dup << 4
when you assign one array it just copies the reference and both of them point to the same reference.
so a change in one is reflected when you print either of them.
orig_array = [1,2,3,4]<br>
another_array = orig_array
puts orig_array.unshift(0).inspect
puts another_array.inspect
Output:
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4]
To avoid this you can use Marshal to copy from original array without impacting the object to which it is copied.
Any changes in the original array will not change the object to which it is copied.
orig_array = [1,2,3,4]<br>
another_array = Marshal.load(Marshal.dump(orig_array))
puts orig_array.unshift(0).inspect
puts another_array.inspect
Output:
[0, 1, 2, 3, 4]
[1, 2, 3, 4]
I have already pasted this answer in another thread. Change value of a cloned object

How to remember swapped array elements in another array?

This is driving me crazy! I've been trying to write a Ruby method to find all permutations, to solve Project Euler's problem 24. When I swap the elements of an array, they are swapped properly. But when I try to STORE this swapped array in a DIFFERENT array, this new array only remembers the latest copy of my swapped array! It won't remember the older version.
When I print out a during the loop, it shows all permutations properly. But when I print out perm (which I use to store all different permutations of a), it only shows 1 version of a repeated several times. How do I fix this?
a = [0, 1, 2, 3]
perms = []
p "a = #{a}" # output: "a = [0, 1, 2, 3]"
perms << a # add a to perms array
p "perms = #{perms}" # output: "perms = [[0, 1, 2, 3]]"
a[0], a[1] = a[1], a[0] # swap 1st 2 elements of a
p "a = #{a}" # output: "a = [1, 0, 2, 3]"
perms << a # add a to perms array
p "perms = #{perms}" # "perms = [[1, 0, 2, 3], [1, 0, 2, 3]]"
a[1], a[2] = a[2], a[1] # swap 2nd 2 elements of a
p "a = #{a}" # "a = [1, 2, 0, 3]"
perms << a # add a to perms array
p "perms = #{perms}" # "perms = [[1, 2, 0, 3], [1, 2, 0, 3], [1, 2, 0, 3]]"
Thanks to Sawa below, both "dup" and "clone" methods solved my problem! Why doesn't my original way work? When would I use "dup" vs. "clone"? Please give me some code examples.
a[0], a[1] = a[1], a[0] # swap 1st 2 elements of a
p "a = #{a}" # output: "a = [1, 0, 2, 3]"
b = a.dup (or a.clone)
perms << b
p "perms = #{perms}" # "perms = [[0, 1, 2, 3], [1, 0, 2, 3]]" *** it remembers!
a[1], a[2] = a[2], a[1] # swap 2nd 2 elements of a
p "a = #{a}" # "a = [1, 2, 0, 3]"
b = a.dup (or a.clone)
perms << b
p "perms = #{perms}" # "perms = [[0, 1, 2, 3], [1, 0, 2, 3], [1, 2, 0, 3]]"
Variables in Ruby (with some exceptions, such as variables bound to integers) contain references to objects, not values. Here's an example from running "irb":
1.9.3p374 :021 > str1="hi"
=> "hi"
1.9.3p374 :022 > str2=str1
=> "hi"
1.9.3p374 :023 > str1.replace("world")
=> "world"
1.9.3p374 :024 > str2
=> "world"
You'll notice that once I replace the value for str1, str2's "value" changes as well. That's because it contains a reference to the str1 object. I know one difference between dup and clone has to do with the "freeze" method. If I had called str1.freeze, then it would prevent the object str1 references from being modified, e.g.,
1.9.3p374 :055 > str1.freeze
=> "hi"
1.9.3p374 :056 > str1[0]="b"
RuntimeError: can't modify frozen String
from (irb):56:in `[]='
from (irb):56
from /.rvm/rubies/ruby-1.9.3-p374/bin/irb:13:in `<main>
"Dup"-ing a frozen object doesn't create a frozen object whereas cloning does.
EDIT: just a slight update....When assigning an object on the right to a variable on the left (e.g., str = Object.new), the variable receives an object reference. When assigning one variable to another, the left-hand side variable receives a copy of the reference that the variable on the right contains. In either case, you are still storing object references in the left-hand side variable.
Your original didn't work because you kept modifying the same array instance a.
Take a dup of the original array each time before you modify it into a different array. Or, create a new instance of Array by not relying on a destructive method.
a = original_array
b = a.dup
... # do some modifications to `b`
perms << b
c = a.dup
... # do some modifications to `c`
perms << c
...
If you don't like reinventing the wheel, you can use the facets gem.
gem install facets
https://github.com/rubyworks/facets/blob/d96ec0d700d1d7180ccbb5452e0a926386ec0b32/lib/backport/facets/array/permutation.rb
require 'facets'
[1, 2, 3].permutation
#=> [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]

Resources