Reputation: 47
In the following lines of code, the outcome I was expecting is different from the actual result. Can someone help me understand why?
array1 = [["a", "b", "c"], ["a", "b", "c"]]
temp1 = ["x", "y", "z"]
array1 << temp1
2.times do
temp1[0] = gets.chomp #first loop enter 1 and then 4
temp1[1] = gets.chomp #first loop enter 2 and then 5
temp1[2] = gets.chomp #first loop enter 3 and then 6
puts temp1.inspect
array1 << temp1
puts array1.inspect
# Actual result: [["a", "b", "c"], ["a", "b", "c"], ["4", "5", "6"], ["4", "5", "6"], ["4", "5", "6"]]
# Expected Result: [["a", "b", "c"], ["a", "b", "c"], ["x", "y", "z"], ["1", "2", "3"], ["4", "5", "6"]]
end
Upvotes: 1
Views: 219
Reputation: 110665
Whenever you have problems like this one, it's instructive to add some code to print out the object_id
of each object of interest at each of several steps in the calculation. Each Ruby object has a unique object_id
. The id can be retrieved with the method Object.object_id:
{ "a"=>1 }.object_id
#=> 70225550848400
Let's try it. (I've shortened object_id
's to their last three digits to make it easier to see when they change.)
array1 = [["a", "b", "c"], ["a", "b", "c"]]
puts "1a array1.object_id=#{array1.object_id % 1000}"
puts "1b array1.map(&:object_id)=#{array1.map { |e| e.object_id % 1000 } }"
puts
temp1 = ["x", "y", "z"]
puts "2a temp1.object_id=#{temp1.object_id % 1000}"
array1 << temp1
puts "2b array1=#{array1.inspect}"
puts "2c array1.object_id=#{array1.object_id % 1000}"
puts "2d array1.map(&:object_id)=#{array1.map { |e| e.object_id % 1000 } }"
puts
2.times do
temp1[0] = gets.chomp
temp1[1] = gets.chomp
temp1[2] = gets.chomp
puts "3a temp1=#{temp1.inspect}"
puts "3b temp1.object_id=#{temp1.object_id % 1000}"
array1 << temp1
puts "3c array1=#{array1.inspect}"
puts "3d array1.object_id=#{array1.object_id % 1000}"
puts "3e array1.map(&:object_id)=#{array1.map { |e| e.object_id % 1000 } }"
puts
end
prints
1a array1.object_id=900
1b array1.map(&:object_id)=[0, 920]
2a temp1.object_id=480
2b array1=[["a", "b", "c"], ["a", "b", "c"], ["x", "y", "z"]]
2c array1.object_id=900
2d array1.map(&:object_id)=[0, 920, 480]
1
2
3
3a temp1=["1", "2", "3"]
3b temp1.object_id=480
3c array1=[["a", "b", "c"], ["a", "b", "c"], ["1", "2", "3"], ["1", "2", "3"]]
3d array1.object_id=900
3e array1.map(&:object_id)=[0, 920, 480, 480]
4
5
6
3a temp1=["4", "5", "6"]
3b temp1.object_id=480
3c array1=[["a", "b", "c"], ["a", "b", "c"], ["4", "5", "6"], ["4", "5", "6"],
["4", "5", "6"]]
3d array1.object_id=900
3e array1.map(&:object_id)=[0, 920, 480, 480, 480]
You need to study this carefully.
To understand what's going on it may be helpful to think of an array as a container. What you have done is change the contents of a container, but not the container itself, yet array1
is a list of containers.
To make your code work you merely have to change the container as well as the contents. One simple way is to replace:
temp1[0] = gets.chomp
temp1[1] = gets.chomp
temp1[2] = gets.chomp
with
temp1 = [gets.chomp, gets.chomp, gets.chomp]
array1 = [["a", "b", "c"], ["a", "b", "c"]]
puts "1a array1.object_id=#{array1.object_id % 1000}"
puts "1b array1.map(&:object_id)=#{array1.map { |e| e.object_id % 1000 } }"
puts
temp1 = ["x", "y", "z"]
puts "2a temp1.object_id=#{temp1.object_id % 1000}"
array1 << temp1
puts "2b array1=#{array1.inspect}"
puts "2c array1.object_id=#{array1.object_id % 1000}"
puts "2d array1.map(&:object_id)=#{array1.map { |e| e.object_id % 1000 } }"
puts
2.times do
temp1 = [gets.chomp, gets.chomp, gets.chomp]
puts "3a temp1=#{temp1.inspect}"
puts "3b temp1.object_id=#{temp1.object_id % 1000}"
array1 << temp1
puts "3c array1=#{array1.inspect}"
puts "3d array1.object_id=#{array1.object_id % 1000}"
puts "3e array1.map(&:object_id)=#{array1.map { |e| e.object_id % 1000 } }"
puts
end
prints
1a array1.object_id=100
1b array1.map(&:object_id)=[220, 120]
2a temp1.object_id=660
2b array1=[["a", "b", "c"], ["a", "b", "c"], ["x", "y", "z"]]
2c array1.object_id=100
2d array1.map(&:object_id)=[220, 120, 660]
1
2
3
3a temp1=["1", "2", "3"]
3b temp1.object_id=800
3c array1=[["a", "b", "c"], ["a", "b", "c"], ["x", "y", "z"], ["1", "2", "3"]]
3d array1.object_id=100
3e array1.map(&:object_id)=[220, 120, 660, 800]
4
5
6
3a temp1=["4", "5", "6"]
3b temp1.object_id=580
3c array1=[["a", "b", "c"], ["a", "b", "c"], ["x", "y", "z"], ["1", "2", "3"],
["4", "5", "6"]]
3d array1.object_id=100
3e array1.map(&:object_id)=[220, 120, 660, 800, 580]
Upvotes: 2
Reputation: 1624
Do this and it will work (add .clone to all references to temp1
):
array1 = [["a", "b", "c"], ["a", "b", "c"]]
temp1 = ["x", "y", "z"]
array1 << temp1.clone
2.times do
temp1[0] = gets.chomp #first loop enter 1 and then 4
temp1[1] = gets.chomp #first loop enter 2 and then 5
temp1[2] = gets.chomp #first loop enter 3 and then 6
puts temp1.inspect
array1 << temp1.clone
puts array1.inspect
end
# Actual result: [["a", "b", "c"], ["a", "b", "c"], ["x", "y", "z"], ["1", "2", "3"], ["4", "5", "6"]]
# Expected Result: [["a", "b", "c"], ["a", "b", "c"], ["x", "y", "z"], ["1", "2", "3"], ["4", "5", "6"]]
Basically, when you append temp1
in array1
it occurs by reference rather than by value. So whenever the temp1 will be updated, the corresponding appended entry in array1 will also be automatically updated. To prevent this behaviour you need to clone
/ dup
the object before appending it to the array. clone
/ dup
duplicate the object (so it doesn't have same reference / object_id) and then assigns it.
For a detailed explanation check this post.
Upvotes: 4