-1

At the moment I have a simple app that consumes an external API and allows a user to query and return json. It returns all keys/values but I would only like it to return lat and lon in json, or at least I would only like to show these two in my view. Here is my code:

locationiq_api.rb

class LocationiqApi
  include HTTParty

  BASE_URI = "https://eu1.locationiq.com/v1/search.php"

  attr_accessor :city

  def initialize(api_key, format = "json")
    @options = { key: api_key, format: format }
  end

  def find_coordinates(city)
    self.class.get(BASE_URI, query: @options.merge({ q: city }))
  end

  def handle_error
    if find_coordinates.code.to_i = 200
      find_coordinates.parsed_response
    else
      raise "Couldn't connect to LocationIQ Api"
    end
  end
end 

locations_controller.rb

class LocationsController < ApplicationController

  def index
    @search = LocationiqApi.new("pk.29313e52bff0240b650bb0573332121e").find_coordinates(params[:q])
  end
end

locations.html.erb

<main>
  <h1>Location Search</h1>
  <!-- Button to search find coordinates -->
  <%= form_tag(locations_path, method: :get) do %>
    <%= label_tag(:q, "Search: ") %>
    <%= text_field_tag(:q) %>
    <%= submit_tag("Find coordinates") %>
  <% end %><br>

  <h2>Search results:</h2>

</main>

<%= @search %>

Returned json:

[{"place_id":"100066","licence":"https:\/\/locationiq.com\/attribution","osm_type":"node","osm_id":"107775","boundingbox":["51.3473219","51.6673219","-0.2876474","0.0323526"],"lat":"51.5073219","lon":"-0.1276474","display_name":"London, Greater London, England, SW1A 2DX, United Kingdom","class":"place","type":"city","importance":0.9654895765402,"icon":"https:\/\/locationiq.org\/static\/images\/mapicons\/poi_place_city.p.20.png"}]
Steve
  • 418
  • 1
  • 4
  • 16

2 Answers2

1

Supposing you have a JSON string that looks something like, as an example, this:

"[{\"place_id\":\"100066\",\"lat\":\"51.5073219\",\"lon\":\"-0.1276474\",\"class\":\"place\",\"type\":\"city\"}]"

Then you should be able to do (roughly speaking):

JSON.dump(
  JSON.
    parse(
      "[{\"place_id\":\"100066\",\"lat\":\"51.5073219\",\"lon\":\"-0.1276474\",\"class\":\"place\",\"type\":\"city\"}]"
    ).
    first.
    slice('lat', 'lon')
)

This should give you:

 => "{\"lat\":\"51.5073219\",\"lon\":\"-0.1276474\"}" 

Now, I recall that HTTParty may already convert the API reponse to a Ruby object (array or hash), so that JSON.dump may not be needed. And, there might be other fiddles you would like to do (e.g., you could use with_indifferent_access if you prefer working with symbols as keys instead of strings).

Just to be clear, if you want this to be part of your class method, then you might do something like:

class LocationiqApi
  include HTTParty

  BASE_URI = "https://eu1.locationiq.com/v1/search.php"

  attr_accessor :city

  def initialize(api_key, format = "json")
    @options = { key: api_key, format: format }
  end

  def find_coordinates(city)
    JSON.dump(
      JSON.
        parse(
          self.class.get(BASE_URI, query: @options.merge({ q: city }))
        ).
        first.
        slice('lat', 'lon')
    )
  end

  def handle_error
    if find_coordinates.code.to_i = 200
      find_coordinates.parsed_response
    else
      raise "Couldn't connect to LocationIQ Api"
    end
  end
end

If self.class.get(BASE_URI, query: @options.merge({ q: city })) returns anything other than a valid JSON string that represents an array of hashes, then this will probably fail.

I suppose the key bits are:

  • You need to convert (parse) your JSON into something Ruby can work with;
  • Once parsed, you need to grab a hash from your array of hashes (assuming an array of hashes is always the result of the parsing);
  • You can use slice to use only those key-value pairs you want;
  • Then, since you stipulated you want to return JSON, you need to turn your Ruby object back into JSON using, for example JSON.dump. You could also use, I suppose, to_json. Whichever floats your boat.
jvillian
  • 19,953
  • 5
  • 31
  • 44
  • thanks for the answer :) So there is no way of doing this in my class method? Somehow just fetching the `lat` and `lon` parameters only? It seems like there must be a simpler solution - maybe using something other than HTTParty that will return JSON rather than a Ruby object. – Steve Apr 24 '19 at 17:25
  • All of that could absolutely take place in your class method. Whether or not you can simply fetch just the `lat` and `lon` parameters depends on what is supported by the API - about which I know nothing. If, however, you are receiving a JSON string that has more attributes than you want, then I don't see how you would get just the attributes you want without doing some parsing, manipulation, and dumping. – jvillian Apr 24 '19 at 17:34
  • I think the [API](https://locationiq.com/docs) supports fetching individual parameters, but not 100% sure. I'm pretty new to working with APIs so it's a bit of a learning curve at the moment. – Steve Apr 24 '19 at 17:50
  • I just took a quick look at the API docs. It does not appear to me that you can specify a subset of the attributes to return. BTW and out of curiosity, why do you want to display JSON in the view? – jvillian Apr 24 '19 at 17:59
  • I'm doing a coding challenge and it stipulates that I should make an app that receives an address as a string and converts it to coordinates and that the endpoint should have a "JSON-formatted response" – Steve Apr 24 '19 at 18:07
-1

Try this:

@search.map {|r| {lat: r['lat'], lon: r['lon']} }

Will be an array of hashes