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!