Convert a standard JSON array into a 2d array object - ruby

In ruby, how would I parse this JSON into a 2d location array object?
I want to convert it to a simple object like this:
[["Seattle"]["Washington"],["Seaton"]["Illinois"]]
I tried a few things and having trouble with it and there are not very many examples of this that I could find via Google search.
{
"data": [
{
"city": "Seattle",
"state": "Washington",
"zip": "98104",
"country": "US",
"empty": false,
"county": null
},
{
"city": "Seaton",
"state": "Illinois",
"zip": "61476",
"country": "US",
"empty": false,
"county": null
}
]
}
Here is what I tried so far (which doesn't get me quite there):
require 'rubygems'
require 'json'
...
parsed = JSON.parse(string)
parsed["data"].each do |location|
unless location["city"].nil?
location.each do |location_item|
puts location_item.inspect
end
end
end

I would do something like this:
require 'json'
JSON.parse(string)['data'].map { |hash| [hash['city'], hash['state']] }
#=> [["Seattle", "Washington"],["Seaton","Illinois"]]

require 'json'
Depending on your requirements,
JSON.parse(str)["data"].flat_map { |h| h.values_at("city", "state") }
#=> ["Seattle", "Washington", "Seaton", "Illinois"]
or
JSON.parse(str)["data"].map { |h| h.values_at("city", "state") }
#=> [["Seattle", "Washington"], ["Seaton", "Illinois"]]

Related

How to select objects from JSON file and push into new file when they fail API validation

I am working with an API which accepts some JSON objects (sent as post request) and fails others based on certain criteria.
I am trying to compile a "log" of the objects which have failed and ones which have been validated successfully so I don't have to manually copy and paste them each time. (There are hundreds of objects).
Basically if the API returns "false", I want to push that object into a file, and if it returns true, all those objects go into another file.
I have tried to read a bunch of documentation / blogs on "select, detect, reject" etc enumerators but my problem is very different from the examples given.
I have written some pseudo code in my ruby file below and I think I'm going along the right lines, but need a bit of guidance to complete the task:
restaurants = JSON.parse File.read('pretty-minified.json')
restaurants.each do |restaurant|
create_response = HTTParty.post("https://api.hailoapp.com/business/create",
{
:body => restaurant.to_json,
:headers => { "Content-Type" => "text", "Accept" => "application/x-www-form-urlencoded", "Authorization" => "token #{api_token}" }
})
data = create_response.to_hash
alert = data["valid"]
if alert == false
# select restaurant json objects which return false and push into new file
# false_rest = restaurants.detect { |r| r == false }
File.open('false_objects.json', 'w') do |file|
file << JSON.pretty_generate(false_rest)
else
# select restaurant json objects which return true and push into another file
File.open('true_objects.json', 'w') do |file|
file << JSON.pretty_generate()
end
end
An example of the output (JSON) from the API is as follows:
{"id":"102427","valid":true}
{"valid":false}
The JSON file is basically an huge array of hashes (or objects), here is a short excerpt:
[
{
"id": "223078",
"name": "3 South Place",
"phone": "+442032151270",
"email": "3sp#southplacehotel.com",
"website": "",
"location": {
"latitude": 51.5190536,
"longitude": -0.0871038,
"address": {
"line1": "3 South Place",
"line2": "",
"line3": "",
"postcode": "EC2M 2AF",
"city": "London",
"country": "UK"
}
}
},
{
"id": "210071",
"name": "5th View Bar & Food",
"phone": "+442077347869",
"email": "waterstones.piccadilly#elior.com",
"website": "http://www.5thview.com",
"location": {
"latitude": 51.5089594,
"longitude": -0.1359897,
"address": {
"line1": "Waterstone's Piccadilly",
"line2": "203-205 Piccadilly",
"line3": "",
"postcode": "W1J 9HA",
"city": "London",
"country": "UK"
}
}
},
{
"id": "239971",
"name": "65 & King",
"phone": "+442072292233",
"email": "hello#65king.com",
"website": "http://www.65king.com/",
"location": {
"latitude": 51.5152533,
"longitude": -0.1916538,
"address": {
"line1": "65 Westbourne Grove",
"line2": "",
"line3": "",
"postcode": "W2 4UJ",
"city": "London",
"country": "UK"
}
}
}
]
Assuming you want to filter by emails, ending with elior.com (this condition might be easily changed):
NB! The data above looks like a javascript var, it’s not a valid ruby object. I assume you just got it from somewhere as a string. That’s why json:
require 'json'
array = JSON.parse(restaurants) # data is a string: '[{....... as you received it
result = array.group_by do |e|
# more sophisticated condition goes here
e['email'] =~ /elior\.com$/ ? true : false
end
File.open('false_objects.json', 'w') do |file|
file << JSON.pretty_generate(result[false])
end
File.open('true_objects.json', 'w') do |file|
file << JSON.pretty_generate(result[true])
end
There is a hash in result, containing two elements:
#⇒ {
# true: [..valids here ..],
# false: [..invalids here..]
# }

Ruby: Can't extract JSON data by nested key using group_by method

I am trying to extract JSON objects which include Hotel in line1 of the address, but I keep getting the following error:
line1_hotel.rb:5:in `block in <main>': undefined method `[]' for nil:NilClass (NoMethodError)
from line1_hotel.rb:4:in `each'
from line1_hotel.rb:4:in `group_by'
from line1_hotel.rb:4:in `<main>'
My Ruby version is 2.1.4p265, and my code is below. I have used square bracket notation to access data in nested keys before. But in this case it seems to be failing. I've looked at the group_by Ruby doc but there is no detail at all whether it accepts this kind of notation. Also, if I don't nest it works in other examples.
require 'json'
array = JSON.parse File.read('gaps4.json')
result = array.group_by do |e|
e['address']['line1'] =~ /Hotel/ ? true : false
end
File.open('testtrue.json', 'w') do |file|
file << JSON.pretty_generate(result[true])
end
File.open('testfalse.json', 'w') do |file|
file << JSON.pretty_generate(result[false])
end
An example "snippet" from the JSON data I am trying to extract. For example one object has Hotel in line1 whereas the other doesn't. (There are many records):
[
{
"id": "242595",
"name": "San Lorenzo - Wimbledon",
"phone": "+442089468463",
"email": "live#sanlorenzo.com",
"website": "https://sanlorenzosw19.squarespace.com/new-page/",
"location": {
"latitude": 51.4221176,
"longitude": -0.208713,
"address": {
"line1": "38 Wimbledon Hill Road",
"line2": "",
"line3": "",
"postcode": "SW19 7PA",
"city": "London",
"country": "UK"
}
}
},
{
"id": "101055",
"name": "Sanderson",
"phone": "+442073005588",
"email": "restaurant.resuk#mhgc.com",
"website": "",
"location": {
"latitude": 51.51747,
"longitude": -0.13724,
"address": {
"line1": "Sanderson Hotel",
"line2": "50 Berners Street",
"line3": "",
"postcode": "W1T 3NG",
"city": "London",
"country": "UK"
}
}
}
]
e['address']['line1'] should be e['location']['address']['line1']. Just re-check your json structure.
The reason you get an error is that e['address'] is nil and e['address']['line1'] is a try to call ['line'], that is in fact #[] method, on nil.
Plus, whether you are not producing this json yourself, it would be fine to protect the code from accidental errors:
e['location'] && # make sure location given
e['location']['address'] && # make sure address given
e['location']['address']['line1'] =~ /Hotel/ ? true : false
Just not to fail if no location was presented.

Transferring JSON Data into an array using ruby

This is my JSON code
{
"jobs": [
{
"id": 1,
"title": "Software Developer",
"applicants": [
{
"id": 1,
"name": "Rich Hickey",
"tags": ["clojure", "java", "immutability", "datomic", "transducers"]
},
{
"id": 2,
"name": "Guido van Rossum",
"tags": ["python", "google", "bdfl", "drop-box"]
}
]
},
{
"id": 2,
"title": "Software Architect",
"applicants": [
{
"id": 42,
"name": "Rob Pike",
"tags": ["plan-9", "TUPE", "go", "google", "sawzall"]
},
{
"id": 2,
"name": "Guido van Rossum",
"tags": ["python", "google", "bdfl", "drop-box"]
},
{
"id": 1337,
"name": "Jeffrey Dean",
"tags": ["spanner", "BigTable", "MapReduce", "deep learning", "massive clusters"]
}
]
}
]
}
I want to put the list of "Jobs" in an array using ruby.
I have the following code so far.
require 'json'
file = File.read(filepath)
data_hash = JSON.parse(file)
How do I iterate on the data_hash and chose what information I want and place it in an array?
You can use Array#each because data_hash['jobs'] contains an array of jobs:
data_hash['jobs'].each {|job| ... }
Like this,
arr = Array.new
data_hash.each { |job|
arr.insert(job['name'])
}
use Array#map for shorter code
data_hash['jobs'].map do |job|
# Do whatever you want with the job here
properties = %w(title applicants)
job.select{ |key| properties.include?(key) }
end

Ruby deep hash looping?

I have a large nested hash in the form below. I need to loop through and pull out the name and url of each repository, but I can't seem to do that. Any suggestions?
Code snippet:
repo_json = get_touched_repos()
repo_hash = JSON.parse(repo_json)
puts repo_hash.class
puts repo_hash['repositories'][0]['name']
The hash:
{
"repositories": [
{
"type": "repo",
"username": "...",
"name": "....",
"owner": "...",
"homepage": "",
"description": "description",
"language": "Java",
"watchers": 2,
"followers": 2,
"forks": 1,
"size":
"open_issues": 0,
"score": 1.0,
"has_downloads": true,
"has_issues": true,
"has_wiki": true,
"fork": false,
"private": false,
"url": "http://my.domain.com/repo/name",
"created": "2012-07-02T17:47:54Z",
"created_at": "2012-07-02T17:47:54Z",
"pushed_at": "2014-03-20T20:09:38Z",
"pushed": "2014-03-20T20:09:38Z"
},
{....}
]
}
You can use Array#each method to do this
repo_hash['repositories'].each do |repo|
puts repo['name']
puts repo['url']
end
To get the names and the URLs in a hash:
name_url_pairs = repo_hash['repositories'].collect do |repo|
{ name: repo['name'], url: repo['url] }
end
Update: Returning a small hash with several extracted values.
Another approach to index by name:
name_hash = Hash[
repo_hash['repositories'].collect do |repo|
[ repo['name'], repo['url'] ]
end
]

Iterate through JSON respone Facebook Graph

Im trying to iterate through a returned response from the Facebook Graph api
def get_feed
uri = URI(FACEBOOK_URL)
response = HTTParty.get(uri)
results = JSON.parse(response.body)['data']
puts formatted_data(results)
end
def formatted_data(results)
return unless results
formatted = results['data'].each do |d|
unless d.nil?
{
message: d['message'],
}
end
formatted.delete_if {|x| x.nil?}
end
end
The response is very large so here is a snippet if it helps
{
"data": [
{
"id": "197958940234297_827831980580320",
"from": {
"category": "Amateur sports team",
"category_list": [
{
"id": "189018581118681",
"name": "Sports Club"
},
{
"id": "139721016091877",
"name": "Outdoor Recreation"
},
{
"id": "109615542448700",
"name": "Physical Fitness"
}
],
"name": "Varsity Vandals",
"id": "197958940234297"
},
"to": {
"data": [
{
"id": "668983363",
"name": "Heather Walker"
},
{
"id": "638195502",
"name": "Emma Williams"
},
{
"id": "1286337937",
"name": "Becky Williams"
}
]
},
"with_tags": {
"data": [
{
"id": "668983363",
"name": "Heather Walker"
},
{
"id": "638195502",
"name": "Emma Williams"
},
{
"id": "1286337937",
"name": "Becky Williams"
}
]
},
"message": "Great turnout for the women's intro session today. Cool to have a women's game and a men's game running side by side. Touch is for all.",
"picture": "https://fbcdn-photos-f-a.akamaihd.net/hphotos-ak-prn2/t1.0-0/1507550_827829843913867_410211203232735862_s.jpg",
"link": "https://www.facebook.com/photo.php?fbid=827829843913867&set=pcb.827831980580320&type=1&relevant_count=2",
"icon": "https://fbstatic-a.akamaihd.net/rsrc.php/v2/yz/r/StEh3RhPvjk.gif",
"actions": [
{
"name": "Comment",
"link": "https://www.facebook.com/197958940234297/posts/827831980580320"
},
{
"name": "Like",
"link": "https://www.facebook.com/197958940234297/posts/827831980580320"
}
],
"privacy": {
"value": ""
},
I am getting an error
TypeError: no implicit conversion of String into Integer
At the moment i would just like to pull out all the Messages from the JSON object...Am i handling the extraction correctly
Any help appreciated
Thanks
I tried you code, I change you require is move formatted.delete_if {|x| x.nil?} out of loop, like following, as formatted will be nil inside the loop.
def formatted_data(results)
return unless results
formatted = results['data'].each do |d|
unless d.nil?
{
message: d['message'],
}
end
end
formatted.delete_if {|x| x.nil?}
end
are you sure your not using the data key twice?
results = JSON.parse(response.body)['data'] in main method and formatted = results['data'].each in your formatted_data method?
Thinking maybe?
def def formatted_data(results)
return unless results
results['data'].map {|m| {message: m['message']} }.compact
end
I'd do this:
def get_feed
uri = URI(FACEBOOK_URL)
response = HTTParty.get(uri)
messages = format_data(response)
for message in messages do
puts message
end
end
def format_data(response, new_data = [])
if response.present?
results = JSON.parse(response)
for result in results do
new_data << result[:data][:message] if result[:data][:message].present?
end
return new_data #-> array of messages
end
end

Resources