2

I'm trying to display the inner levels of the hash from my JSON request.

This is the return from JSON request:

{
    "results": [
    {
    "utc_offset": -25200000,
    "venue": {
      "zip": "94305",
      "country": "us",
      "localized_country_name": "USA",
      "city": "Stanford",
      "address_1": "750 Escondido Road",
      "name": "Ray's - Graduate Community Center",
      "lon": -122.158386,
      "id": 5612552,
      "state": "CA",
      "lat": 37.423393,
      "repinned": false
    },
    "status": "upcoming"
    },
    {
    "utc_offset": -25200000,
    "venue": {
      "zip": "94306",
      "country": "us",
      "localized_country_name": "USA",
      "city": "Palo Alto",
      "address_1": "25 Churchill Ave",
      "name": "Palo Alto High School",
      "lon": -122.154121,
      "id": 1566333,
      "state": "CA",
      "lat": 37.433186,
      "repinned": false
    },
    "status": "upcoming"
    },
    {
    "utc_offset": -25200000,
    "venue": {
      "zip": "94306",
      "country": "us",
      "localized_country_name": "USA",
      "city": "Palo Alto",
      "address_1": "25 Churchill Ave",
      "name": "Palo Alto High School",
      "lon": -122.154121,
      "id": 1566333,
      "state": "CA",
      "lat": 37.433186,
      "repinned": false
    },
    "status": "upcoming"
    }
    ]
    }
}

I've referred to: How to use JSON data returned by API call in Rails 4, How to get a second level field from json file using ruby, Why do I get "no implicit conversion of String into Integer (TypeError)"?, How to access JSON in Rails?

Which helped me get to this point, I can display the inner levels of the hash if I specify the array index using @result = @api_response["results"][0]["venue"]["name"] which returns "Ray's - Graduate Community Center". This is what I am trying to display, but I wanted to display the name for all the venue on the list and not just the name at index [0]. But I'm not sure how to iterate through the index.

I can also display the hashed results by using @result = @api_response["results"] and iterate through using:

<% @result.each do |i| %>
  <%= i["venue"] %>
<% end %> 

Which returns:

{"zip"=>"94305", "country"=>"us", "localized_country_name"=>"USA", "city"=>"Stanford", "address_1"=>"750 Escondido Road", "name"=>"Ray's - Graduate Community Center", "lon"=>-122.158386, "id"=>5612552, "state"=>"CA", "lat"=>37.423393, "repinned"=>false} 
{"zip"=>"94305", "country"=>"us", "localized_country_name"=>"USA", "city"=>"Stanford", "address_1"=>"750 Escondido Road", "name"=>"Ray's - Graduate Community Center", "lon"=>-122.158386, "id"=>5612552, "state"=>"CA", "lat"=>37.423393, "repinned"=>false} 
{"zip"=>"94306", "country"=>"us", "localized_country_name"=>"USA", "city"=>"Palo Alto", "address_1"=>"25 Churchill Ave", "name"=>"Palo Alto High School", "lon"=>-122.154121, "id"=>1566333, "state"=>"CA", "lat"=>37.433186, "repinned"=>false} 
{"zip"=>"94306", "country"=>"us", "localized_country_name"=>"USA", "city"=>"Palo Alto", "address_1"=>"25 Churchill Ave", "name"=>"Palo Alto High School", "lon"=>-122.154121, "id"=>1566333, "state"=>"CA", "lat"=>37.433186, "repinned"=>false} 

{"zip"=>"94306", "country"=>"us", "localized_country_name"=>"USA", "city"=>"Palo Alto", "address_1"=>"25 Churchill Ave", "name"=>"Palo Alto High School", "lon"=>-122.154121, "id"=>1566333, "state"=>"CA", "lat"=>37.433186, "repinned"=>false} 
{"zip"=>"94306", "country"=>"us", "localized_country_name"=>"USA", "city"=>"Palo Alto", "address_1"=>"25 Churchill Ave", "name"=>"Palo Alto High School", "lon"=>-122.154121, "id"=>1566333, "state"=>"CA", "lat"=>37.433186, "repinned"=>false} 

But when I try using this:

<% @result.each do |i| %>
  <%= i["venue"]["name"] %> <br />
<% end %> 

I get a undefined method[]' for nil:NilClass` error, it does not let me access the "name" inside of "venue" which I am trying to display.

Am I on the right track? Is there a better way to display the inner levels of the hash? Any feedback would help. Thanks!

Community
  • 1
  • 1
teresa
  • 356
  • 5
  • 21

2 Answers2

2

Are you sure that every object in the results array has a venue field?

Before trying to access the venue field, make sure to see if the object actually exists. In your controller, add this

@result = @api_response["results"].reject {|obj| obj.nil?}

This will remove any object in the array which is nil.

What if the object exists but the field venue doesn't?

<% @result.each do |i| %>
  <%= i.has_key? "venue" ? i["venue"]["name"] : 'Location unspecified' %> <br />
<% end %> 

If this looks messy, you could also move it to a helper method like

def venue_name(obj)
  obj.has_key? "venue" ? obj["venue"]["name"] : 'Location unspecified'
end

And then make use of it in the view.

 <% @result.each do |i| %>
  <%= venue_name(i) %> <br/>
 <% end %> 
Arun Kumar Mohan
  • 11,517
  • 3
  • 23
  • 44
  • Thanks for the reply. I tried `<% @result.each do |i| %> <%= i.has_key? "venue" ? i["venue"]["name"] : 'Location unspecified' %>
    <% end %> ` and still got `undefined method `[]' for nil:NilClass` error. But I do understand now that the `venue` might be missing for some of the `results` hence the error.
    – teresa Sep 07 '16 at 23:09
  • @tshckr Thats strange. Are you able to display `i["venue"]`? – Arun Kumar Mohan Sep 07 '16 at 23:10
  • Yup, that seems to work, but I get newline randomly which I think that's where the `venue` field may be `nil`? I edited my question above to add more lines to what was returned from `i["venue"]` – teresa Sep 07 '16 at 23:14
  • @tshckr I have updated my answer. Let me know if it helps! – Arun Kumar Mohan Sep 07 '16 at 23:39
  • Sorry, I tried it but received the same `nil` error. These answers provided from Shadwell: `@result.select { |r| r.present? && r.has_key?('venue') }.each do |i|` and `<%= i.fetch('venue', {}).fetch('name', 'No venue') %>` did work. But I am not trying to ignore the results with no `venue` I want to display them as well except with a "TBD". Thank you for your feedback. – teresa Sep 09 '16 at 22:55
  • @tshckr, thanks for trying my solution. I think you misinterpreted my answer. My code only rejects objects which are nil and not objects which don't have a `venue` field. Remember you saw newlines when you tried to display objects in the array? Thats because one of the objects was `nil`. This is necessary because `obj.has_key? "venue"` can only be done if `obj` is not `nil`. Thanks again! – Arun Kumar Mohan Sep 09 '16 at 23:07
1

Looks like you are on the right track; it's just that there's a result in there that is either nil or that doesn't have a venue within it.

How you approach the problem depends on what you want to do those situations.

You could filter the results to exclude results without a venue. It would be more efficient to do this as part of your query but you could just do:

@result.select { |r| r.present? && r.has_key?('venue') }.each do |i|

Alternatively you could output "no venue" or somesuch if the venue is missing. I tend to use fetch in situations like this. You can try to fetch the value from the hash but return a default if the key isn't in the hash. So, in your case, you could do:

<%= i.fetch('venue', {}).fetch('name', 'No venue') %>

This looks for the venue hash in the result but returns an empty hash if it isn't there. The second fetch then tries to retrieve the name from that hash and again returns 'No venue' if it couldn't find it.

Shadwell
  • 34,314
  • 14
  • 94
  • 99
  • Thank you for your explanation, they both work really well. With the `fetch`, I was able to see which entries had `'No venue'`. One question, is it possible to add the `'No venue'` message to this `@result.select { |r| r.present? && r.has_key?('venue') }.each do |i|` ? – teresa Sep 07 '16 at 23:28