How to use default value for hash of empty arrays?

I want to use the default value for reset my ary when I need to. But I can not understand how not to change the default values ​​when changing ary values.

> default = {"a"=>[], "b"=>[], "c"=>[]} => {"a"=>[], "b"=>[], "c"=>[]} > ary = default.clone => {"a"=>[], "b"=>[], "c"=>[]} > ary["a"] << "foo" => ["foo"] > default => {"a"=>["foo"], "b"=>[], "c"=>[]} 
+6
source share
6 answers

What you found here is that Hash#clone only executes a small clone, that is, it replicates itself, not the objects it refers to.

There are many β€œdeep clones” that touch on this particular issue, or you can write your own to get around it:

 class Hash def deep_clone Hash[collect { |k,v| [ k, v.respond_to?(:deep_clone) : v.deep_clone : v ] }] end end class Array def deep_clone collect { |v| v.respond_to?(:deep_clone) : v.deep_clone : v } end end 

This will allow you to clone arbitrary Hash and Array objects as needed.

+7
source

Both clone and duplexer create a shallow copy of your object, which leads to this behavior. I'm not sure what the correct way to achieve a deep copy is, but instead:

 ary = default.clone 

Try:

 ary = Marshal.load(Marshal.dump(default)) 

This is taken from live environment 2.3.8 on ruby ​​1.8.7

+3
source

clone only makes small copies, so cloning your hash still holds everything that points to the same nested arrays.

You can avoid this through the Marshal class by dropping and then loading the values ​​of the object:

 > default = {"a" => [], "b" => [], "c" => []} => {"a"=>[], "b"=>[], "c"=>[]} > ary = Marshal.load(Marshal.dump(default)) => {"a"=>[], "b"=>[], "c"=>[]} > ary["a"] << "foo" => ["foo"] > default => {"a"=>[], "b"=>[], "c"=>[]} 
+2
source
 class Object def deep_clone Marshal::load(Marshal.dump(self)) end end default = {"a"=>[], "b"=>[], "c"=>[]} ary = default.deep_clone ary["a"] << "foo" default {"a"=>[], "b"=>[], "c"=>[]} 
+2
source

The way to do this is as follows:

 ary = Marshal.load(Marshal.dump(default)) 
+2
source

Depending on what you want to do, a simpler alternative to writing a deep clone method might be to write a method that creates a new default array every time it is called:

 def default {"a"=>[], "b"=>[], "c"=>[]} end ary = default #=> {"a"=>[], "b"=>[], "c"=>[]} ary["a"] << "foo" #=> {"a"=>["foo"], "b"=>[], "c"=>[]} default #=> {"a"=>[], "b"=>[], "c"=>[]} 

Of course, if the contents of the hash change by default during the program, this will not work, and you will need to learn cloning or sorting methods, but if the contents are fixed, this may be a more direct solution.

+2
source

Source: https://habr.com/ru/post/887663/


All Articles