0

temp gets @board.dup, and @board array is modified. However, temp gets modified as well! I have tried reading all the related documentations but still couldn't figure out an explanation.

class Test
    def initialize
        @board = [[1,2],[3,4], [5,6]]
    end

    def modify
        temp = @board.dup #Also tried .clone

        print 'temp: ';p temp
        print '@board: ';p @board

        @board.each do |x|
            x << "x"
        end

        print "\ntemp: ";p temp
        print '@board: ';p @board
    end
end

x = Test.new
x.modify

Output:

temp: [[1, 2], [3, 4], [5, 6]]
@board: [[1, 2], [3, 4], [5, 6]]

temp: [[1, 2, "x"], [3, 4, "x"], [5, 6, "x"]] # <= Why did it change?
@board: [[1, 2, "x"], [3, 4, "x"], [5, 6, "x"]]

What can I do to ensure temp doesn't get modified?

3
  • It's the same reference, e.g., temp is pointing at the same array @board is. If you want to maintain the original then you'll have to clone it. Commented Aug 12, 2016 at 11:37
  • Sorry, edited my question. I did do @board.dup in my code. But temp got changed as well. (also tried with .clone, still the same output) Commented Aug 12, 2016 at 11:39
  • You have to clone deep. Commented Aug 12, 2016 at 14:05

4 Answers 4

1

You have Array with Arrays, so you dup the first array but, inside object point to the same instance. In this case you just modify the same source.

like here:

arr = [[1, 2, 3]]
arr2 = arr.dup

arr2[0] << 1

p arr
# => [[1, 2, 3, 1]]
p arr2
# => [[1, 2, 3, 1]]

So you must use dup for all array instance like this.

arr = [[1, 2, 3]]
arr3 = arr.map(&:dup)
arr3[0] << 1

p arr
# => [[1, 2, 3]]
p arr3
# => [[1, 2, 3, 1]]

In your case use this map.

class Test
  def initialize
    @board = [[1,2],[3,4], [5,6]]
  end

  def modify
    temp = @board.map(&:dup) # dup all

    print 'temp: ';p temp
    print '@board: ';p @board

    @board.each do |x|
      x << "x"
    end

    print "\ntemp: ";p temp
    print '@board: ';p @board
  end
end

x = Test.new
x.modify
# temp: [[1, 2], [3, 4], [5, 6]]
# @board: [[1, 2], [3, 4], [5, 6]]
# 
# temp: [[1, 2], [3, 4], [5, 6]]
# @board: [[1, 2, "x"], [3, 4, "x"], [5, 6, "x"]]
Sign up to request clarification or add additional context in comments.

Comments

0

The reason is clone and dup produce a shallow copy. Thus the object ids of the inner arrays are still the same: those are the same arrays.

What you need is a deep clone. There is no built-in method in the standard library able to do that.

Comments

0

From the docs:

dup produces a shallow copy of obj—the instance variables of obj are copied, but not the objects they reference.

That said: You will need to make a deep-copy of your array.

When you are using the ActiveSupport gem (Rails) you can use its deep_dup method instead of just calling dup:

temp = @board.deep_dup

Without gems mashaling might be a easy solution to deep dup almost everything, without knowing about the internals of an object:

temp = Marshal.load(Marshal.dump(@board))  

4 Comments

Marshal is OP in this case.
@LukasBaliak: but there's nothing better in vanilla ruby.
@SergioTulentsev For full dump all deep object yes, But in this case i think a litle OP.
@LukasBaliak: I kind of agree with you. But I prefer to write answers that do not only work for a specify example. Other readers in the future might have a slightly different problem and might be interested in a more generalized solution.
0

You can add a deep_dup method for nested arrays:

class Array
  def deep_dup
    map {|x| x.deep_dup}
  end
end

# To handle the exception when deepest array contains numeric value
class Numeric
  def deep_dup
    self
  end
end

class Test
    def initialize
        @board = [[1,2], [3,4], [5,6]]
    end

    def modify
        temp = @board.deep_dup
        ...
    end
end

x = Test.new
x.modify

# temp: [[1, 2], [3, 4], [5, 6]]
# @board: [[1, 2], [3, 4], [5, 6]]

# temp: [[1, 2], [3, 4], [5, 6]]
# @board: [[1, 2, "x"], [3, 4, "x"], [5, 6, "x"]]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.