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

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

Related

Why a new call of a method with exclamation mark affects all previous calls of that method?

I'm sorry if this is a duplicate - I couldn't find anything similar in the existing posts.
I understand the difference between methods like shuffle and shuffle!. However, I am confused why calling the method more than once would result in changing the variables of all objects that previously referred to it? I'd expect once we apply a method, that the variable gets a value and we're done with it. Not that it continues to refer to the method call and the argument passed and that it would get re-evaluated later on.
I thought it's best to demonstrate with an example:
irb(main):001:1* def shuffle(arr)
irb(main):002:1* arr.shuffle!
irb(main):003:0> end
=> :shuffle
irb(main):004:0> arr = [1,2,3,4]
=> [1, 2, 3, 4]
irb(main):005:0> one = shuffle(arr)
=> [4, 2, 3, 1]
irb(main):006:0> two = shuffle(arr)
=> [1, 2, 4, 3]
irb(main):007:0> one
=> [1, 2, 4, 3]
So, here I'd expect one to stay [4, 2, 3, 1]. However, with each new call, all previous ones would get equated to the latest result of the method call. I realise it should have something to do with calling it with the same argument arr, but still doesn't quite make sense.
Array#shuffle! shuffles the array in-place and returns its receiver:
ary = [1, 2, 3, 4]
ary.equal?(ary.shuffle!) #=> true
Assigning the result from shuffle! to another variable doesn't change this. It merely results in two variables referring to the same array:
a = [1, 2, 3, 4]
b = a.shuffle!
a #=> [2, 4, 1, 3]
b #=> [2, 4, 1, 3]
a.equal?(b) #=> true
You probably want a new array. That's what Array#shuffle (without !) is for:
a = [1, 2, 3, 4]
b = a.shuffle
a #=> [1, 2, 3, 4]
b #=> [2, 4, 1, 3]
Even if shuffle returns the element in the original order, you'll get another array instance:
a = [1, 2, 3, 4]
b = a.shuffle until b == a
a #=> [1, 2, 3, 4]
b #=> [1, 2, 3, 4]
a.equal?(b) #=> false

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.

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]

Returning Multiple Values From Map

Is there a way to do:
a = b.map{ |e| #return multiple elements to be added to a }
Where rather than returning a single object for each iteration to be added to a, multiple objects can be returned.
I'm currently achieving this with:
a = []
b.map{ |e| a.concat([x,y,z]) }
Is there a way to this in a single line without having to declare a = [] up front?
Use Enumerable#flat_map
b = [0, 3, 6]
a = b.flat_map { |x| [x, x+1, x+2] }
a # => [0, 1, 2, 3, 4, 5, 6, 7, 8]
Use Enumerable#flat_map
Which is probably not much different than:
p [1, 2, 3].map{|num| [1, 2, 3]}.flatten
--output:-
[1, 2, 3, 1, 2, 3, 1, 2, 3]

Resources