arr = ["red","green","yellow"]
arr2 = arr.clone
arr2[0].replace("blue")
puts arr.inspect
puts arr2.inspect
produces:
["blue", "green", "yellow"]
["blue", "green", "yellow"]
Is there anyway to do a deep copy of an array of strings, other than using Marshal as i understand that is a hack.
I could do:
arr2 = []
arr.each do |e|
arr2 << e.clone
end
but it doesn't seem very elegant, or efficient.
Thanks
Your second solution can be shortened to arr2 = arr.map do |e| e.dup end
(unless you actually need the behaviour of clone
, it's recommended to use dup
instead).
Other than that your two solutions are basically the standard solutions to perform a deep copy (though the second version is only one-level deep (i.e. if you use it on an array of arrays of strings, you can still mutate the strings)). There isn't really a nicer way.
Edit: Here's a recursive deep_dup method that works with arbitrarily nested arrays:
class Array
def deep_dup
map {|x| x.deep_dup}
end
end
class Object
def deep_dup
dup
end
end
class Numeric
# We need this because number.dup throws an exception
# We also need the same definition for Symbol, TrueClass and FalseClass
def deep_dup
self
end
end
You might also want to define deep_dup for other containers (like Hash), otherwise you'll still get a shallow copy for those.
I recommend your initial idea, but written slightly more concisely:
arr = ["red","green","yellow"]
arr2 = arr.inject([]) { |a,element| a << element.dup }
I am in a similar situation and very concerned about speed. The fastest way for me was to make use of map{&:clone}
So try this:
pry(main)> a = (10000..1000000).to_a.shuffle.map(&:to_s)
pry(main)> Benchmark.ms { b = a.deep_dup }
=> 660.7760030310601
pry(main)> Benchmark.ms { b = a.join("--!--").split("--!--") }
=> 605.0828141160309
pry(main)> Benchmark.ms { b = a.map(&:clone) }
=> 450.8283680770546
You can make a deep copy of array a
by following code:
Marshal.load(Marshal.dump(a))
It looks so simple.. Just run the below code:
a = [1,2,3]
b = [].replace(a)
b[1] = 5
puts a
puts b
Run above code and you will notice the difference. Cheers !
You can use this hack:
arr1 = %w{ red green blue }
arr2 = arr1.join("--!--").split("--!--")
But it is just for fun :)
arr2[0].replace("lol")
p arr1
#=> ["red", "green", "blue"]
p arr2
#=> ["lol", "green", "blue"]
And it will work only for 1 level arrays