1

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
3
  • 1
    You're appending and modifying the same array. Commented Jun 27, 2016 at 1:40
  • Why is it not sequential even if i am appending to the same array? (so the first loop I append 1,2,3 to array1 and then append 4,5,6) (i'm a novice) Commented Jun 27, 2016 at 1:48
  • 1
    No, you're appending and mutating the same array. You append it once when it's 123. Then you change it to 456, which means the one you already appended is changed to 456, and then you append it again. Commented Jun 27, 2016 at 10:31

2 Answers 2

4

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Am glad that it helped : )
2

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]

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.