Feb 12, 2017
“Neatly” dealing with JSON.parse-d Hashes in Ruby
So you use some REST API that responds with JSON with a ton of nested objects and arrays, an example of this is the Google Maps Direction API.
All you’re interested in is a value that is deep within the hash-array tunnel.
{
"status": "OK",
"geocoded_waypoints" : [
{
"geocoder_status" : "OK",
"place_id" : "ChIJ7cv00DwsDogRAMDACa2m4K8",
"types" : [ "locality", "political" ]
},
{
"geocoder_status" : "OK",
"place_id" : "ChIJ69Pk6jdlyIcRDqM1KDY3Fpg",
"types" : [ "locality", "political" ]
},
{
"geocoder_status" : "OK",
"place_id" : "ChIJgdL4flSKrYcRnTpP0XQSojM",
"types" : [ "locality", "political" ]
},
{
"geocoder_status" : "OK",
"place_id" : "ChIJE9on3F3HwoAR9AhGJW_fL-I",
"types" : [ "locality", "political" ]
}
],
"routes": [ {
"summary": "I-40 W",
"legs": [ {
"steps": [ {
"travel_mode": "DRIVING",
"start_location": {
"lat": 41.8507300,
"lng": -87.6512600
},
"end_location": {
"lat": 41.8525800,
"lng": -87.6514100
},
"polyline": {
"points": "a~l~Fjk~uOwHJy@P"
},
"duration": {
"value": 19,
"text": "1 min"
},
...oh there's more, so much more...
}
For example, say we’re interested in the duration value (it is 19 in the example response above). To access this you’ll have to do this
response_hash = JSON.parse(response) duration_in_seconds = response_hash["routes"][0]["legs"][0]["duration"]["value"]
Now imagine you had to access the duration value for many different responses and probably compare or sort them. You’ll find that you’ll have a lot of response_hash["routes"][0]["legs"][0]["duration"]["value"] sprinkled everywhere.

To solve this problem we’re going to use two nifty things Ruby provides:
1. Hash#dig
.dig this is a method that was introduced in Ruby 2.30 for hashes and arrays.
duration_in_seconds = response_hash.dig("routes", 0, "legs", 0, "duration", "value")Pretty simple right?
You can access a nested value by passing the keys and indexes as arguments in the correct order.
2. The splat operator “*”
The splat operator converts an array into a list of arguments.

You can have an array a = [1, 2, 3] and covert it into a list of arguments for some_method like this:
some_method(*a)
This is equivalent to :
some_method(1, 2, 3)
So for our use case, you can save the arguments of .dig as an array and use it anywhere you want without having to retype/copy and paste that ugly line of square brackets and quotation marks.
response_hash = JSON.parse(response) nested_keys = ["routes", 0, "legs", 0, "duration", "value"] duration_in_seconds = response_hash.dig(*nested_keys)
You can use .dig(*nested_keys) anywhere in your code when you’re trying to access the duration value.
It’ll make everything look a bit cleaner.