If hash [: a] [: b] [: c] = 0 in Ruby, I want you to extend it recursively even if the key does not exist.

TL;DR

I want to do something like unix's mkdir -p with a Ruby hash

hash = Hash.new {|h,k| h[k] = h.class.new(&h.default_proc) }
hash[:a][:b][:c] = 'Good!!'
p hash # {:a=>{:b=>{:c=>"Good!!"}}}

Commentary

About Hash.new

Hash.new takes the hash itself and the key, and can define a default value in the block if the contents of the key do not exist.

hash1 = Hash.new { |h, k| 0 }

hash1[:a] = 100
p hash1[:a] # 100
p hash1[:b] # 0

The above simply defines a hash that returns 0 if the key is not found. Since : a is set, it refers to the contents of the hash, and since: b does not exist, the block is executed and 0 is returned.

Set a default value for the hash

Taking advantage of the property that the block is executed only if the key is not found, you can apply the default value to the hash itself as follows:

hash2 = Hash.new { |h, k| h[k] = 0 }

hash2[:a] += 100
hash2[:b] += 200

p hash2 # {:a=>100, :b=>200}

In the following, from the undefined state of both : a``: b, addition processing is performed using + =, but when hash2 [: a] hash2 [: b] is evaluated The default value of 0 is set in the hash itself, so 100 and 200 are set brilliantly.

Extend the hash with default values

Further application is to allow hash digging even when the keys are nested.

hash3 = Hash.new { |h,k| h[k] = {} }

hash3[:a][:b] = 100
p hash3 # {:a=>{:b=>100}

The block is executed when hash3 [: a] is referenced, and the hash after hash3 [: a] = {} is applied is returned. On the other hand, [: b] = 100 is executed, so the hash is finally nested.

However, if this is left as it is, it will fail when the nesting is tripled.

hash3 = Hash.new { |h,k| h[k] = {} }

hash3[:a][:b][:c] = 100 # undefined method `[]=' for nil:NilClass (NoMethodError)

That's because hash3 [: a] sets the default value, but it's just a {}, which itself doesn't define a default value setting.

Therefore, hash3 [: a] [: b] is just a hash with no default value, so if you try to refer to hash3 [: a] [: b] [: c], you will get an error. is.

Extend the hash recursively with default values

The hash3 [: a] [: b] in the previous section has become a normal hash with no default value, but I would like it to have a similar default value.

To write it straightforwardly, it is as follows.

hash4 = Hash.new { |h,k| h[k] = Hash.new { |h,k| h[k] = {} } }

hash4[:a][:b][:c] = 100
p hash4 # {:a=>{:b=>{:c=>100}}}

We were able to handle even Mie Nest. However, of course, it cannot support quadruple nesting. It's hell.

hash4 = Hash.new { |h,k| h[k] = Hash.new { |h,k| h[k] = {} } }

hash4[:a][:b][:c][:d] = 100 # undefined method `[]=' for nil:NilClass (NoMethodError)

No matter how deep the nesting is, we want the hash to be the default value, which recursively firmly defines the default value.

You can use Hash # default_proc. default_proc can get the block of default value definition set in the hash with the proc object.

proc can be executed by the call method, so if you look at the following, you will get an image.

hash5 = Hash.new { |k, v| 'default value'}

p hash5.default_proc.call(nil, nil) # "default value"

And the block can be passed in the form & proc, so you can write it like this:

hash6 = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
hash6[:a][:b][:c][:d] = 100

p hash6 # hash6[:a][:b][:c][:d] = 100

Did you get hungry?

hash6 makes Hash, which applies definition of its own default value, to its own default value. This meant that the definition of the default value would always be inherited, no matter how many nests there were.

Remarks

After writing it, I noticed that there was an article with a commentary on a similar story. Well, I will post it in the sense of organizing what I learned.

-Nest hash infinitely -Define a recursively accessible Hash in Ruby

Recommended Posts

If hash [: a] [: b] [: c] = 0 in Ruby, I want you to extend it recursively even if the key does not exist.
MockMVC returns 200 even if I make a request to a path that does not exist
I want to create a Parquet file even in Ruby
I want to Flash Attribute in Spring even if I set a reverse proxy! (do not do)
Handling when calling a key that does not exist in hash
[Ruby] I want to extract only the value of the hash and only the key
I want to get the value in Ruby
If you want to recreate the instance in cloud9
Even if I want to convert the contents of a data object to JSON in Java, there is a circular reference ...
[Ruby] When you want to assign the result obtained by conditional branching to a variable and put it in the argument
What to do if you installed Ruby with rbenv but the version does not change
Even in Java, I want to output true with a == 1 && a == 2 && a == 3
Even if I write the setting of STRICT_QUOTE_ESCAPING in CATALINA_OPTS in tomcat8.5, it is not reflected.
[Ruby] I want to reverse the order of the hash table
What to do if you select a JRE in Eclipse and get "The selected JRE does not support the current compliance level 11"
If you want to mock a method in RSpec, you should use the allow method for mock and the singleton method.
[Ruby] I want to put an array in a variable. I want to convert to an array
If you want to include the parent class in Lombok's @builder
[Java] I want to perform distinct with the key in the object
I want to change the value of Attribute in Selenium of Ruby
When installing a gem with C extension in Ruby, I want to finish it quickly using multiple CPU cores like make -j4
I want to download a file on the Internet using Ruby and save it locally (with caution)
An active hash that can be treated as data even if it is not in the database
A story that even a man who does not understand C language could add new functions to Ruby 2.6
What to do if Operation not permitted is displayed when you execute a command in the terminal
What to do if the prefix c is not bound in JSP
Even in Java, I want to output true with a == 1 && a == 2 && a == 3 (PowerMockito edition)
I thought about the best way to create a ValueObject in Ruby
If you want to make a zip file with Ruby, it's rubyzip.
I want you to use Enum # name () for the Key of SharedPreference
Update if the document already exists in Azure Cosmos DB Java SDK, create new if it does not exist
Continuation ・ Active hash that can be handled as data even if it is not in the database ~ Display
If it is Ruby, it is efficient to make it a method and stock the processing.
If you want to satisfy the test coverage of private methods in JUnit
How to make @Transactional work that does not work if you use it incorrectly
Even in Java, I want to output true with a == 1 && a == 2 && a == 3 (Javassist second decoction)
You may not want to use the remove method in ArrayList very often
I want to recursively get the superclass and interface of a certain class
Even in Java, I want to output true with a == 1 && a == 2 && a == 3 (black magic edition)
Even in Java, I want to output true with a == 1 && a == 2 && a == 3 (gray magic that is not so much as black magic)
I want to use arrow notation in Ruby
[Ruby] I want to do a method jump!
I made a Ruby extension library in C
I want to be eventually even in kotlin
[Ruby] I want to make a program that displays today's day of the week!
If you want to know the options when configuring Ruby, see `RbConfig :: CONFIG ["configure_args "]`
I want to morphologically analyze the log in the DB and put it in the DB to classify messages 1
[ruby] How to assign a value to a hash by referring to the value and key of another hash
[Rails] I want to display the link destination of link_to in a separate tab
7 things I want you to keep so that it doesn't become a fucking code
[Maven] What to do if you are asked to incorporate a jar that is not in the remote repository into the war
At the time of dialogReturn event, I checked because it is not updated even if I specify a component with update
I want to embed any TraceId in the log
I want to use a little icon in Rails
AtCoder Beginner Contest 170 A, B, C up to ruby
I want to define a function in Rails Console
I want to click a GoogleMap pin in RSpec
I want to add a delete function to the comment function
[Ruby on Rails] I want to get the URL of the image saved in Active Storage
[Ruby on Rails] credential does not work in production environment even though I saved production.key
After posting an article with Rails Simple Calendar, I want to reflect it in the calendar.