Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use Array#dig and Hash#dig introduced in Ruby 2.3?

Tags:

arrays

ruby

hash

Ruby 2.3 introduces a new method on Array and Hash called dig. The examples I've seen in blog posts about the new release are contrived and convoluted:

# Hash#dig user = {   user: {     address: {       street1: '123 Main street'     }   } }  user.dig(:user, :address, :street1) # => '123 Main street'  # Array#dig results = [[[1, 2, 3]]] results.dig(0, 0, 0) # => 1 

I'm not using triple-nested flat arrays. What's a realistic example of how this would be useful?

UPDATE

It turns out these methods solve one of the most commonly-asked Ruby questions. The questions below have something like 20 duplicates, all of which are solved by using dig:

How to avoid NoMethodError for missing elements in nested hashes, without repeated nil checks?

Ruby Style: How to check whether a nested hash element exists

like image 481
user513951 Avatar asked Dec 18 '15 00:12

user513951


2 Answers

One way would be in conjunction with the splat operator reading from some unknown document model.

some_json = JSON.parse( '{"people": {"me": 6, ... } ...}' ) # => "{"people" => {"me" => 6, ... }, ... } a_bunch_of_args = response.data[:query] # => ["people", "me"] some_json.dig(*a_bunch_of_args) # => 6 
like image 24
acsmith Avatar answered Sep 27 '22 03:09

acsmith


In our case, NoMethodErrors due to nil references are by far the most common errors we see in our production environments.

The new Hash#dig allows you to omit nil checks when accessing nested elements. Since hashes are best used for when the structure of the data is unknown, or volatile, having official support for this makes a lot of sense.

Let's take your example. The following:

user.dig(:user, :address, :street1) 

Is not equivalent to:

user[:user][:address][:street1] 

In the case where user[:user] or user[:user][:address] is nil, this will result in a runtime error.

Rather, it is equivalent to the following, which is the current idiom:

user[:user] && user[:user][:address] && user[:user][:address][:street1] 

Note how it is trivial to pass a list of symbols that was created elsewhere into Hash#dig, whereas it is not very straightforward to recreate the latter construct from such a list. Hash#dig allows you to easily do dynamic access without having to worry about nil references.

Clearly Hash#dig is also a lot shorter.


One important point to take note of is that Hash#dig itself returns nil if any of the keys turn out to be, which can lead to the same class of errors one step down the line, so it can be a good idea to provide a sensible default. (This way of providing an object which always responds to the methods expected is called the Null Object Pattern.)

Again, in your example, an empty string or something like "N/A", depending on what makes sense:

user.dig(:user, :address, :street1) || "" 
like image 52
Drenmi Avatar answered Sep 26 '22 03:09

Drenmi