-1

I used an inner array (or hash) in nested blocks to save data:

p "#1 both inner_arr and outer_arr set empty outside of both loops"
outer_arr = []
inner_arr = []
i = 0
2.times{
  j = 0
  2.times{
    inner_arr[j] = j+100+i 
    j += 1
  }
  p inner_arr
  outer_arr[i] = inner_arr
  i += 1
  p outer_arr
}
p "======================================================"
p "#2 outer_arr set empty before both loops while inner_arr set empty inside of outer loop and outside of inner loop"
outer_arr_2 = []
i = 0
2.times{
  j = 0
  inner_arr_2 = []
  2.times{
    inner_arr_2 << j+100+i 
    j += 1
  }
  p inner_arr_2
  outer_arr_2[i] = inner_arr_2
  i += 1
  p outer_arr_2
}
p "======================================================"
p "#3 both outer and inner hash set empty outside of both loops"
outer_hash_3 = {}
inner_hash_3 = {}
i = 0
2.times{
  j = 0
  2.times{
    inner_hash_3 [j] = j+100+i 
    j += 1
  }
  p inner_hash_3
  outer_hash_3[i] = inner_hash_3
  i += 1
  p outer_hash_3
}
p "======================================================"
p "#4 outer_hash set empty before both loops while inner_hash set empty inside of outer loop and outside of inner loop"
outer_hash_4 = {}
i = 0
2.times{
  j = 0
  inner_hash_4 = {}
  2.times{
    inner_hash_4[j] = j+100+i 
    j += 1
  }
  p inner_hash_4
  outer_hash_4[i] = inner_hash_4
  i += 1
  p outer_hash_4
}

If I didn't reset the inner array (or hash) when the inner array (or hash) changed, it updated data I had saved in the outer array. If I reset the inner array (or hash), then it won't update the outer array. I don't understand why this is happening. Please help me understand it.

sawa
  • 165,429
  • 45
  • 277
  • 381
Wenya
  • 1
  • 4
    this is really too much code for us to go through. If you're unsure of one area, please trim it down and state "I expected this to happen (with some output) but instead this happened (with some output)" – Anthony May 29 '15 at 15:35
  • 1
    What is your question? – Jeff Price May 29 '15 at 15:37
  • My question is I don't reset my inner_arr inside of the outer loop, it goes back to update the information I saved in the outer arr earlier - which is a strange behavior. If you execute p "#1 both inner_arr and outer_arr set empty outside of both loops" outer_arr = [] inner_arr = [] i = 0 2.times{ j = 0 2.times{ inner_arr[j] = j+100+i j += 1 } p inner_arr outer_arr[i] = inner_arr i += 1 p outer_arr } It returns: [[101, 102], [101, 102]] instead of [[100, 101], [101, 102]] - the expected outcome – Wenya May 29 '15 at 15:40
  • Maybe there should there maybe be a catch-all for these type of questions. I feel like almost every other question I see about strange behavior in Ruby these days stems from the fact that assignment doesn't create a copy of the assigned object, just another reference to it. Kind of like http://stackoverflow.com/q/588004/1157054 – Ajedi32 May 29 '15 at 15:42
  • Please edit the question itself in order to make it succinct and clear. The question should stand alone, without needing comments to prop it up. – Wayne Conrad May 29 '15 at 18:03

1 Answers1

1

You've discovered something here, but I guess you haven't quite fully absorbed the ramifications of it.

The second form is the correct way to do it. This is because in the first case you're recycling the same array for each iteration, every time you insert it you're inserting the same object over and over.

This is best demonstrated here:

a = [ 1 ]
b = [ a, a ]
# => [[1],[1]]

a << 2
# => [1,2]

b
# => [[1, 2], [1, 2]]

When you insert something into another structure you're not making a copy, you're adding what is technically a pointer to that data. In this case b contains two pointers to the same a. Any modifications made to a will appear to affect every element in b, but really the elements are identical by definition due to the way they were inserted.

If you're aware of this, that assigning variables and adding objects to structures almost never results in a copy being made, most of the time this works in your favour. If you do need to make a copy, methods like dup or clone usually help.

Re-writing your simple test in a more Ruby fashion not only side-steps the problem, but makes it a lot simpler:

(0..1).collect do |i|
  (0..1).collect do |j|
    j + 100 + i
  end
end

Ruby's Enumerable library is what makes a lot of this possible. Try to think of a solution to your problem as a series of transforms on your data.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • Thank you for your answer. In the example you provided, b is changed to [[1, 2], [1, 2]] because a << 2. But if we assign [1,2] to a (a = [1,2]), b won't be changed to [[1,2],[1,2]]. Why's is a << 2 different from a = [1,2] – Wenya May 29 '15 at 15:53
  • If you assign a new object to `a`, then you're no longer talking about the same `a` that was inserted into `b`. Since it's a new object, modifications to that *new* `a` will not impact `b`. – tadman May 29 '15 at 15:55