Damon
Damon

Reputation: 4336

Prevent this variable from changing

I am doing a word problem. I am trying to set two arrays. One serves as a benchmark (doesn't change during iteration), the other changes during iteration. Here is an example:

seed = 1
a = [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1]]
bm = a[seed]
a[seed].each.with_index do |x, i|
  if bm.include?(0)
    a[seed][i] = "WRONG"
  else
    a[seed][i] = 0 unless i == 3
  end
end

a #         => [[1, 1, 1, 1], [0, "WRONG", "WRONG, "WRONG"], [1, 1]]
# Should be => [[1, 1, 1, 1], [0, 0, 0, 1], [1, 1]]
bm #        => [0, "WRONG", "WRONG, "WRONG"]
# Should be => [1, 1, 1, 1]

Usually if you want to have a "before and after" in your code you can do:

a = 5
b = a
a = 6
a # => 6
b # => 5

In this case, it doesn't change b when a is changed. Why does this happen? I suspect it's because the second example is storing the number as b whereas in the first example, it's storing the code only. What can I do to fix this? Is this bad practice?

Upvotes: 1

Views: 80

Answers (1)

Philip Hallstrom
Philip Hallstrom

Reputation: 19879

You're right. When you assign the array to bm you're really assigning a reference (or pointer, not sure what Ruby prefers to call it) to a[seed]. You can see this by printing out the object_id of both variables:

> a[seed].object_id
=> 70347648205960
> bm.object_id
=> 70347648205960

Note that they are pointing to the same internal object. The solution is to use dup to duplicate the array and assign the new one to bm2.

> bm2 = a[seed].dup
=> [1, 1, 1, 1]
> bm2.object_id
=> 70347649948520

Note the object_id has changed. And now if I make a change...

> a[seed][0] = 'WRONG'
=> "WRONG"
> a[seed]
=> ["WRONG", 1, 1, 1]
> bm
=> ["WRONG", 1, 1, 1]
> bm2
=> [1, 1, 1, 1]

You might want to Google to read about object_id, dup, and also clone which is similar to dup, but has some differences.

Upvotes: 1

Related Questions