Is there a simple way to duplicate a multi-dimensional array in Ruby?

Viewed 4251

I have a 2-dimensional array in Ruby that I want to produce a working duplicate of. Obviously I can't do this;

array=[[3,4],[5,9],[10,2],[11,3]]
temp_array=array

as any modifications I make to temp_array will also be made to array, as I have merely copied the object identifier. I thought I would be able to get around this by simply using;

temp_array=array.dup

but this doesn't work as temp_array is simply an array of object identifiers that get duplicated so I still end up modifying the initial array (if I understand what went wrong when I did this). The solution I found was to do the following;

temp_array=[]
array.each{|sub| temp_array << sub.dup}

This achieves what I want but seems to be an awkward way of solving my problem.

I am concerned about how this would work if I didn't know what my array was going to be containing (e.g. if it was possible that some parts of the array had 3-dimensions). I would potentially have to test the class of each member of the array to see if it had to be iterated over in order to duplicate it. Not an impossible task at all, but it seems messy to me. Is this simply a consequence of Ruby lacking built-in support for multidimensional arrays or is there a simple built-in function to do this that I have missed?

7 Answers

Here's the "Ruby-esque" way to handle it:

temp_array = Marshal.load(Marshal.dump(your_array_to_be_cloned))

As other people have pointed out, you can use clone. This will not work, however, as it's a shallow copy, so the sub arrays (this is not really a multidimensional array, I think) will not get cloned. Since arrays are mutable objects in Ruby, the sub arrays will get changed. For instance, check this out

>> blah = [[3,5],6]
=> [[3, 5], 6]
>> joe = blah.clone
=> [[3, 5], 6]
>> joe[0]
=> [3, 5]
>> joe[0].push "blah"
=> [3, 5, "blah"]
>> blah
=> [[3, 5, "blah"], 6]

So as you can see, just doing clone will not work. But you knew that, hence your question.

I cooked this up just now. This will do until you find out the real, Ruby way to do it (I just work in Ruby, I'm not an expert).

def dup_recursive(new_array, old_array)
  old_array.each do |item|
    if item.class == Array
      new_array << dup_recursive([], item)
    else
      new_item = item.dup rescue new_item = item # in case it's got no dupe, like FixedNum
      new_array << new_item
    end
    new_array
  end
end

array=[[3,[9,12]],[5,9],[10,2],[11,3]]
new_array = Array.new
dup_recursive(new_array, array)
puts array.inspect
puts new_array.inspect

I know, I'm not using duck-typing, but I'd be glad to be schooled as to how to do this without asking for the class of the object in question.

Edit: I should've just searched on deep-clone ruby in Google, but sometimes I like writing code :)... anyway, the other solution presented -- Marshal.load( Marshal.dump( array ) ) -- will also work for Hashes and so forth, so it's way better.

Try this:

temp_array = array.clone

You can use array.clone as specified here. That will give you a copy of the original object, and not just a pointer.

Related