3

I'm trying to work with the Google Maps geocoding API in Elixir though I'm a bit new to the language so working with nested data structures is eluding me.

I'm using HTTPotion to fetch the JSON from the geocoding endpoint and parsing it into Elixir data structures (a series of nested lists and tuples) using JSEX.

defmodule Geocode do
  def fetch do
    HTTPotion.start
    result = HTTPotion.get "http://maps.googleapis.com/maps/api/geocode/json?address=Fairfax,%20Vermont&sensor=false"
    json   = JSEX.decode result.body
  end
end

The following is now assigned to json.

{:ok, [{"results", [[{"address_components", [[{"long_name", "Fairfax"}, {"short_name", "Fairfax"}, {"types", ["locality", "political"]}], [{"long_name", "Franklin"}, {"short_name", "Franklin"}, {"types", ["administrative_area_level_2", "political"]}], [{"long_name", "Vermont"}, {"short_name", "VT"}, {"types", ["administrative_area_level_1", "political"]}], [{"long_name", "United States"}, {"short_name", "US"}, {"types", ["country", "political"]}]]}, {"formatted_address", "Fairfax, VT, USA"}, {"geometry", [{"bounds", [{"northeast", [{"lat", 44.772892}, {"lng", -72.92268890000001}]}, {"southwest", [{"lat", 44.6330508}, {"lng", -73.08477409999999}]}]}, {"location", [{"lat", 44.6654963}, {"lng", -73.01032}]}, {"location_type", "APPROXIMATE"}, {"viewport", [{"northeast", [{"lat", 44.772892}, {"lng", -72.92268890000001}]}, {"southwest", [{"lat", 44.6330508}, {"lng", -73.08477409999999}]}]}]}, {"types", ["locality", "political"]}]]}, {"status", "OK"}]}

I'd like to extract the latitude and the longitude from this nested data structure. I've tried using pattern matching but as the structure is fairly complex the corresponding pattern is a bit of a nightmare. While the following works it can't possibly be a good solution.

{_, [{_, [[_, _, { _, [{_, [{_, [{_, lat}, {_, lgn}]}, _]}, _, _, _] }, _]]}, _]} = json

So my question is what is the best way to extract values from deeply nested data structures in Elixir? Any solutions or nudges in the right direction would be greatly appreciated.

3 Answers 3

8

Check out the source to Seth Falcon's ej library:

https://github.com/seth/ej

It does more or less what you're wanting to do, using recursion.

I'm not an Elixir expert, but you might be able to just use that library directly.

Sign up to request clarification or add additional context in comments.

1 Comment

You should be able to use ej with Elixir just fine. Tools like ej are the way to go right now but we are aware that accessing and updating nested data structures are not as easy as they are supposed to be and we are working on a proposal to address those issues.
4

The ej library looks like a good choice, but in addition I might cut to the heart of the data structure right away:

{:ok, [{"results", results}, {"status", status}]} = JSEX.decode result.body

or if you will always only use the first result:

{:ok, [{"results", [geo_json | _]}, {"status", status}]} = JSEX.decode result.body

Then I would use the ej library on the geo_json data structure.

Comments

0

Your JSEX.decode(result.body) value formatted more nicely:

{:ok,
 [
   {"results",
    [
      [
        {"address_components",
         [
           [
             {"long_name", "Fairfax"},
             {"short_name", "Fairfax"},
             {"types", ["locality", "political"]}
           ],
           [
             {"long_name", "Franklin"},
             {"short_name", "Franklin"},
             {"types", ["administrative_area_level_2", "political"]}
           ],
           [
             {"long_name", "Vermont"},
             {"short_name", "VT"},
             {"types", ["administrative_area_level_1", "political"]}
           ],
           [
             {"long_name", "United States"},
             {"short_name", "US"},
             {"types", ["country", "political"]}
           ]
         ]},
        {"formatted_address", "Fairfax, VT, USA"},
        {"geometry",
         [
           {"bounds",
            [
              {"northeast", [{"lat", 44.772892}, {"lng", -72.92268890000001}]},
              {"southwest", [{"lat", 44.6330508}, {"lng", -73.08477409999999}]}
            ]},
           {"location", [{"lat", 44.6654963}, {"lng", -73.01032}]},
           {"location_type", "APPROXIMATE"},
           {"viewport",
            [
              {"northeast", [{"lat", 44.772892}, {"lng", -72.92268890000001}]},
              {"southwest", [{"lat", 44.6330508}, {"lng", -73.08477409999999}]}
            ]}
         ]},
        {"types", ["locality", "political"]}
      ]
    ]},
   {"status", "OK"}
 ]}

with/1 seems like a good fit:

with {:ok, json}                              <- JSEX.decode(result.body),
     [{"results", results}, {"status", "OK"}] <- json,
     [[_, _, {"geometry", geometry}, _]]      <- results,
     [_, {"location", location}, _, _]        <- geometry,
     [{"lat", lat}, {"lng", lng}]             <- location                  do

     # Do something with `lat` and `lng` here.

end

Or maybe:

{:ok, lat, lng} =
  with {:ok, json}                              <- JSEX.decode(result.body),
       [{"results", results}, {"status", "OK"}] <- json,
       [[_, _, {"geometry", geometry}, _]]      <- results,
       [_, {"location", location}, _, _]        <- geometry,
       [{"lat", lat}, {"lng", lng}]             <- location                  do

     {:ok, lat, lng}

  else

    _ -> {:error, "Failed to parse Google Maps API `geocode` response."}

  end

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.