Dig'gin through hashes and arrays in Ruby

While working with third party APIs, lot of times we get nested JSON structure in the response. Here is sample response from GitHub API.

[
  {
    "id": 1,
    "title": "Found a bug",
    "body": "I'm having a problem with this.",
    "user": {
      "login": "octocat",
      "id": 1,
    },
    "labels": [
      {
        "name": "bug"   
      }
    ],
    "assignee": {
      "login": "octocat",
    },
    "assignees": [
      {
        "login": "octocat"       
      }
    ],
    "milestone": {      
      "creator": {
        "login": "octocat"
      },
      "open_issues": 4,      
    },
    "pull_request": {
      "url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347"
    },
    "closed_at": null,
    "created_at": "2011-04-22T13:33:48Z",
    "updated_at": "2011-04-22T13:33:48Z"
  }
]

Once we parse this with JSON.parse we get a nested hash object. To reach the creator of the milestone of an

issue, we need to reach it as follows.

issues = JSON.parse(issues_params)
issue.each do |issue|
  creator = issue[:milestone][:creator][:login]
end

Do you spot any problem with this piece of code? If the issue[:milestone] is nil then this code will fail.

We can solve this by checking if issues[:milestone] is not nil then try to access the creator. But what if

issue[:milestone][:creator] is nil?

issues = JSON.parse(issues_params)
issue.each do |issue|
  creator = issue[:milestone] && issue[:milestone][:creator] && issue[:milestone][:creator][:login]
end

Instead we can use my most favorite method from Ruby which is dig.

This method is available from Ruby 2.3 onwards.

Digging through nested hashes

Ruby provides a method Hash#dig which can be used in this case. As the name suggests, the method digs through the nested hash looking for keys we asked it to look for. If it finds out any key which is not present, it just returns nil. Let's write above code using the Hash#dig method.


issue.each do |issue|
  creator = issue.dig(:milestone, :creator, :login)
end

If issue[:milestone] or issue[:milestone][:creator] is nil then this method will simply return nil instead of resulting into an error.

Digging arrays

Array class in Ruby also provides dig method.

h = { colors:  ["red", "blue", "yellow", "green"] }
irb(main):013:0> h.dig(:colors, 0)
=> "red"
irb(main):014:0> h.dig(:colors, 2)
=> "yellow"

Liked this article about handling nested hashes and arrays in Ruby? You will like my other articles about Ruby and Rails as well. Don't miss any of my blogs by subscribing to my newsletter!