Whats the difference between 'input = self' and 'input = self.dup' - ruby

So the problem I'm having is understanding the difference between = self and = self dup. When I run the code below any array I put into it is permanently changed.
class Array
def pad(min_size, value = nil)
input = self
counter = min_size.to_i - self.length.to_i
counter.times do
input.push(value)
end
input
end
end
But then I noticed if I put input = self.dup it would not permanently change my array. Can someone explain why? Thanks!
class Array
def pad(min_size, value = nil)
input = self.dup
counter = min_size.to_i - self.length.to_i
counter.times do
input.push(value)
end
input
end
end

Check their object_id which will give you answer,by saying that self and self#dup are 2 different object.
class Array
def dummy
[self.object_id,self.dup.object_id]
end
def add
[self.push(5),self.dup.push(5)]
end
end
a = [12,11]
a.dummy # => [80922410, 80922400]
a.add # => [[12, 11, 5], [12, 11, 5, 5]]
a # => [12, 11, 5]

Because self.dup returns a shallow copy of the array and so the code will not change the array permanently.
Reference

When you made a duplicate of your array, you ended up with two separate arrays that had the same elements in them, like in this example:
x = [1, 2, 3]
y = [1, 2, 3]
x << 4
p x
p y
--output:--
[1, 2, 3, 4]
[1, 2, 3]
You wouldn't expect changes to the x array to affect the y array, would you? Similarly:
arr = [1, 2, 3]
puts "arr = #{arr}" #arr = [1, 2, 3
copy = arr.dup
puts "copy = #{copy}" #copy = [1, 2, 3]
arr << 4
puts "arr = #{arr}" #arr = [1, 2, 3, 4]
puts "copy = #{copy}" #copy = [1, 2, 3]
copy << "hello"
puts "arr = #{arr}" #arr = [1, 2, 3, 4]
puts "copy = #{copy}" #copy = [1, 2, 3, "hello"]
In that example, arr plays the role of self, and copy plays the role of self.dup. The arrays self and self.dup are different arrays which happen to have the same elements.
An array can have an unlimited number of variables that refer to it:
arr = [1, 2, 3]
puts "arr = #{arr}" #arr = [1, 2, 3]
input = arr
input << 4
puts "arr = #{arr}" #arr = [1, 2, 3, 4]
Now let's make input refer to another array:
copy = arr.dup
input = copy
input << "hello"
puts "copy = #{copy}" #copy = [1, 2, 3, 4, "hello"]
puts "input = #{input}" #input = [1, 2, 3, 4, "hello"]
puts "arr = #{arr}" #arr = [1, 2, 3, 4]

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.

Ruby Getting a max value out of a newly created array in one function

I want my function to return the longest Array within a nested array (including the array itself) so
nested_ary = [[1,2],[[1,2,[[1,2,3,4,[5],6,7,11]]]],[1,[2]]
deep_max(nested_ary)
=> [1,2,3,4,[5],6,7,11]
simple_ary = [1,2,3,4,5]
deep_max(simple_ary)
=> returns: [1,2,3,4,5]
I created a function to collect all arrays. I have to get the max value in another function.
my code:
def deep_max(ary)
ary.inject([ary]) { |memo, elem|
if elem.is_a?(Array)
memo.concat(deep_max(elem))
else
memo
end }
end
This gives me what I want:
deep_max(nested_ary).max_by{ |elem| elem.size }
Is there a way to get this max inside of the function?
def deep_max(arr)
biggest_so_far = arr
arr.each do |e|
if e.is_a?(Array)
candidate = deep_max(e)
biggest_so_far = candidate if candidate.size > biggest_so_far.size
end
end
biggest_so_far
end
deep_max [[1, 2], [[1, 2, [[1, 2, 3, 4, [5], 6, 7, 11]]]], [1, [2]]]
#=> [1, 2, 3, 4, [5], 6, 7, 11]
You can unroll it:
def deep_max(ary)
arys = []
ary = [ary]
until ary.empty?
elem = ary.pop
if elem.is_a?(Array)
ary.push(*elem)
arys.push(elem)
end
end
arys.max_by(&:size)
end
Or you can cheat, by introducing an optional parameter that changes how your recursion works on top level vs how it behaves down the rabbit hole.

ruby** reverse array without using reverse method

This is my array and custom method to reverse an array output without using the reverse method. not sure where it broke, tried running it in console, no dice.
numbers = [1, 2, 3, 4, 5, 6]
def reversal(array)
do |item1, item2| item2 <=> item1
end
p reversal(numbers)
Here's one way to handle this. This is not very efficient but works.
def reversal(array)
reversed = []
loop do
reversed << array.pop
break if array.empty?
end
reversed
end
Here is another implementation that does the same thing:
def reversal(array)
array.each_with_index.map do |value, index|
array[array.count-index-1]
end
end
So many ways... Here are three (#1 being my preference).
numbers6 = [1, 2, 3, 4, 5, 6]
numbers5 = [1, 2, 3, 4, 5]
For all methods my_rev below,
my_rev(numbers6)
#=> [6, 5, 4, 3, 2, 1]
my_rev(numbers5)
#=> [5, 4, 3, 2, 1]
#1
def my_rev(numbers)
numbers.reverse_each.to_a
end
#2
def my_rev(numbers)
numbers.each_index.map { |i| numbers[-1-i] }
end
#3
def my_rev(numbers)
(numbers.size/2).times.with_object(numbers.dup) do |i,a|
a[i], a[-1-i] = a[-1-i] , a[i]
end
end
there are so many ways to do this
1 Conventional way
a=[1,2,3,4,5,6,7,8]
i=1
while i <= a.length/2 do
temp = a[i-1]
a[i-1] = a[a.length-i]
a[a.length-i] = temp
i+=1
end
2 Using pop
a=[1,2,3,4,5,6]
i=0
b=[]
t=a.length
while i< t do
b << a.pop
i+=1
end
3 Using pop and loop
a=[1,2,3,4,5,6]
b=[]
loop do
b << a.pop
break if a.empty?
end
a = [1,2,3,4,5]
b = []
a.length.times { |i| b << a[(i+1)*-1] }
b
=> [5,4,3,2,1]

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

Change value of a cloned object

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]

Resources