Reputation: 1614
An empty array is defined outside a block and within the block, it is assigned to a new variable. If the new array changes, the empty array also modifies its values. Why?
# THIS CODE CHECK WHICH LETTERS IN klas APPEAR IN docs
klas = ["a", "b", "c"]
docs = [[1, "a"], [2, "a"], [3, "b"], [4, "b"], [5, "c"], [6, "c"]]
output = []
empty_array = Array.new(klas.size) { 0 } # EMPTY ARRAY DEFINED HERE
docs.each do |doc|
puts empty_array.inspect
puts (output_row = empty_array).inspect # EMPTY ARRAY ASSIGNED TO ANOTHER
# FIND INDEX OF THE LETTER IN klas FROM docs. ASSIGN 1.
output_row[klas.index(doc[1])] = 1 # PROBLEM! THIS ALSO CHANGES THE EMPTY ARRAY VALUES
output << output_row
end
CONSOLE OUTPUT to show empty array changing its value based on another array
###
empty_array is [0, 0, 0]
output_row is [0, 0, 0]
---
###
empty_array is [1, 0, 0]
output_row is [1, 0, 0]
---
###
empty_array is [1, 0, 0]
output_row is [1, 0, 0]
---
###
empty_array is [1, 1, 0]
output_row is [1, 1, 0]
---
###
empty_array is [1, 1, 0]
output_row is [1, 1, 0]
---
###
empty_array is [1, 1, 1]
output_row is [1, 1, 1]
---
# INCORRECT output IS
=> [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]
# SHOULD BE
=> [[1, 0, 0], [1, 0, 0], [0, 1, 0], [0, 1, 0], [0, 0, 1], [0, 0, 1]]
But if the empty array is defined within the block and assigned to a new variable it works as expected.
docs.each do |doc|
empty_array = Array.new(klas.size) { 0 } # THIS MAKES SURE empty_array stays with zero values
output_row = empty_array
output_row[klas.index(doc[1])] = 1
output << output_row
end
CORRECT output IS
=> [[1, 0, 0], [1, 0, 0], [0, 1, 0], [0, 1, 0], [0, 0, 1], [0, 0, 1]]
Why is the empty array modifying its value when it is outside a block? Shouldn't it remain the same irrespective of another array changing its value?
Upvotes: 1
Views: 60
Reputation: 4415
By using
output_row = empty_array
you are not copying the empty array. You are creating a reference to the same underlying array that is referenced in empty_array
.
You can create a clone or a duplicate of the array. So use:
output_row = empty_array.dup
This will create a new Array, which is a duplicate of empty_array, see http://ruby-doc.org/core-2.4.1/Object.html#method-i-dup
A more complete explanation on what happens with an example:
a = [1] => [1] # is creating a new array 'x'
# a is referencing this array, a is not the array itself!
b = a => [1] # b now references the array 'x'
a = [2] => [2] # is creating a new array 'y', a is referencing this new array
# the reference of b is not changing
b => [1] # b still pointing to array 'x'
So with b = a
you just tell them to reference the same array x
, but b
is not a reference to a
.
Upvotes: 4