Ruby extract single key value from first block in json - ruby

I'm parsing a very large json output from an application API and end up with a ruby array similar to the sanitized version below:
{"log_entries"=>
[{"id=>"SDF888B2B2KAZZ0AGGB200",
"type"=>"warning",
"summary"=>"Things happened",
"created"=>"2017-07-11T18:40:31Z",
"person"=>
{"id"=>"44bAN8",
"name"=>"Harry"}
"system"=>"local",
"service"=>"syslog"
{"id=>"HMB001NBALLB81MMLLABLK",
"type"=>"info",
"summary"=>"Notice",
"created"=>"2017-06-02T11:23:21Z",
"person"=>
{"id"=>"372z1j",
"name"=>"Sally"}
"system"=>"local",
"service"=>"syslog"}]},
"other"=>200,
"set"=>0,
"more"=>false,
"total"=nil}
I just need to be able to print the value of the "created" key only in the first block. Meaning, when the program exits, I need it to print "2017-07-11T18:40:31Z." I've googled a lot but wasn't successful in finding anything. I've tried something like:
puts ["log_entries"]["id"]["created"]
My expectation was to print all of them to start somewhere and even that yields an error. Forgive me, I don't use ruby much.

Since log_entries is an array you can just access the first element and get its created value.
Assuming the variable result holds the whole hash (the JSON you parse from the API):
puts result['log_entries'][0]['created']
will print out the first date. Now you might want to guard that for cases where log_entries empty, so wrap it in a if:
if result['log_entries'].any?
puts result['log_entries'][0]['created']
end

Your json is not in valid format. But assuming you have the right format, following should work
result["log_entries"].collect{|entry| entry["created"]}
=> ["2017-07-11T18:40:31Z", "2017-06-02T11:23:21Z"]
Above code will collect all the created date and give you an array

Related

unwrapping an object returned from twitter api

While reading some data from the Twitter api, I inserted the data into the file like this
results.each do |f|
running_count += 1
myfile.puts "#{f.user_mentions}"
...
The results (2 sample lines below) look like this in the file
[#<Twitter::Entity::UserMention:0x007fda754035803485 #attrs={:screen_name=>"mr_blah_blah", :name=>"mr blah blah", :id=>2142450461, :id_str=>"2141354324324", :indices=>[3, 15]}>]
[#<Twitter::Entity::UserMention:0x007f490580928 #attrs={:screen_name=>"andrew_jackson", :name=>"Andy Jackson", :id=>1607sdfds, :id_str=>"16345435", :indices=>[3, 14]}>]
Since the only information I'm actually interested in is the :screen_name, I was wondering if there's a way that I could only insert the screen names into the file. Since each line is in array brackets and then I'm looking for the screen name inside the #attrs, I did this
myfile.puts "#{f.user_mentions[0]#attrs{"screen_name"}}"
This didn't work, and I didn't expect it to, as I'm not really sure if that's technically array etc. Can you suggest how it would be done?
You need to access the #attrs instance variable in the Twitter UserMention object. If you want to puts the screen name from the first object, based on your current output, I would write
myfile.puts "#{f.user_mentions[0].attrs[:screen_name]"
Also, putting the code on how results is returned would help get a definite answer quickly. Cheers!
Assuming that results is an array of Twitter::Entity::UserMention
results.each do |r|
myfile.puts r.screen_name
end

Convert dateTime.iso8601 value to human readable date in Ruby

I am playing around with the Red Hat Satellite API. One of the values being returned for an API call is dateTime.iso8601 "last_checkin" - Last time server successfully checked in. I'm not sure how to parse this, though.
This is the code that returns the data:
systems.each do |system|
print "#{system["name"]}" + " " + "#{system["last_checkin"]}" + "\n"
end
It gives a hash back: #<XMLRPC::DateTime:0x97102d8>. At least, I think that's a hash.
What do I need to do to look inside it and extract useful information?
That’s not a Hash, that’s an instance of class XMLRPC::DateTime. Use it’s methods to get printable values, e.g.:
systems.each do |system|
system = system["last_checkin"] # or whatever value you are interested in
puts "#{system.mon}-#{system.day}-#{system.year}"
end
Also please avoid using "a"+"b" string concatenation; there is inplace-evaluation ruby paradigm for that ("#{val}".)

Post issue with sinatra

Following this tutorial to create the route for the POST:
post '/secret' do
params[:secret].reverse
end
When I view this on the local server I get my secret message in reverse. But if I want to print another line, below the reverse line, the output gets all messed up on the localhost:9393.
post '/secret' do
p params[:secret].reverse
p params[:secret].reverse
end
Any ideas why, in the second block of code, only one line of the reversed secret shows up on my output after inputting the secret in the form on the main page? I'm looking to write more complex code but it seems like it's only printing the last line. Is this a Sinatra thing?
In your first example you're seeing your secret in reverse because Sinatra is handed the reversed string from the block and displays it.
In your second bit of code you're printing the reversed strings to the local output, which is where you'd expect to see the logs displayed. You're seeing a messed up string because the result of p params[:secret].reverse is not something Sinatra apparently wants to display correctly. Sinatra will fall back to trying to display the value returned from a route block if no other output had already been built up.
The next section of the tutorial you're following covers Views and Templates. Those are what you'll use to output a webpage.
It's just how Sinatra works. The return value of the post block is sent as response. The return value of the block is always the return value of the last statement inside the block. So your p is actually useless, it will only print the secret to the log. It just happens that p returns the value it was passed after printing it, so you still see one line because that is the return value of the block. If you actually want to return a response that contains the line twice, you have to build a single string that contains two lines. There are many ways to do this, here are some:
# use string interpolation to create a string that
# contains reversed_secret, followed by a newline,
# then again followed by reversed_secret
post '/secret' do
reversed_secret = params[:secret].reverse
"#{reversed_secret}\n#{reversed_secret}"
end
# use an array and join the values with a newline
post '/secret' do
reversed_secret = params[:secret].reverse
[reversed_secret, reversed_secret].join("\n")
end
# "print" individual lines to a StringIO object
# and read the resulting string afterwards
post '/secret' do
result = StringIO.new
reversed_secret = params[:secret].reverse
result.puts reversed_secret
result.puts reversed_secret
result.string
end
Whatever works best for you.
Oh, I see. You're using p.
Unlike PHP, Sinatra does not capture standard output. Rather, what is returned as the content .of the page is what is returned from the function.
In the first example, you return the result of reverse.
In the first example, you return nil, which is the result of the second p call.
If you want to return both reverses, you need to construct a string that contains them both:
post '/secret' do
"#{params[:secret].reverse}\n#{params[:secret].reverse}"
end

Extract JSON values from remote api with Ruby

I'm trying to grab some data from last.fm and use it in a simple sinatra app. I've worked out how to open the document but having issues extracting the data in ruby here is the first list of the API data I'd like to grab the name:
{"similarartists":{"artist":[{"name":"Sonny & Cher"}]}
This is just an extract of the return, I'm using this in my rb file:
require 'json'
require 'open-uri'
data = JSON.parse(open("http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&artist=editors&api_key=xxx&format=json").read)
puts data["similarartists"]["artist"]["name"]
It doesn't seem to be working I get can't convert String into Integer (TypeError) on ruby 1.9.3 but the name in the JSON isn't an integer? If I just put the following:
puts data["similarartists"]["artist"]
It returns the whole thing, but I want to grab inside of that and get the name.
"name"=>"Interpol"
I don't understand why it would complain about integers when the name is a string? Hope someone can help me!
Based on the comments thread, the issue is a misunderstanding of the structure of the data returned from the API call.
The exact issue was the structure had an array of artists under the artist key so to get at the name you need to do:
data['similarartists']['artist'][0]['name']
Note though that you should only do that if you are sure there will only be one artist. The nature of the return data suggests that won't always be the case so you might be better off pulling all names depending on your use doing something like:
data['similarartists']['artist'].map {|a| a['name']}.join(',')
That will join all of the artist names together comma separated.
In the future, you can track this issue down by looking at the full structure of the return data and making sure you see the correct structure. The docs on the API may indicate some help here too.
You also might check if someone has made a gem for accessing the API. Often a gem will up-level some of this raw output and give you a nice object to work with. I suggest searching GitHub for a last.fm gem.
The problem is that you are trying to access an Array with the index "name", Ruby tries to convert this to an Integer and fails which results in the Error message you are seeing.
If you test the class of data["similarartists"]["artist"].class you will see that it returns Array. So basically what is happening is that the JSON.parse() called created as the value of data["similarartists"]["artist"] an Array of Hashes. To access all of the artist names you can simply iterate through this array:
require 'json'
require 'open-uri'
data = JSON.parse(open("http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&artist=editors&api_key=29da5a0e01ca2d1524cac596d5462d67&format=jso\
n").read)
# iterate through the Array of returned artists and print their names
data["similarartists"]["artist"].each do |artist|
puts artist["name"]
end
# output
# Interpol
# White Lies
# The Cinematics
# Smith & Burrows
# The National
# Julian Plenti
# She Wants Revenge
# etc ...
If you only want the first entry for Interpol you can just use index [0]:
puts data["similarartists"]["artist"][0]["name"]

Ruby parse comma separated text file

I need some help with a Ruby script I can call from the console. The script needs to parse a simple .txt file with comma separated values.
value 1, value2, value3, etc...
The values needs to be added to the database.
Any suggestions?
array = File.read("csv_file.txt").split(",").map(&:strip)
You will get the values in the array and use it to store to database. If you want more functions, you can make use of FasterCSV gem.
Ruby 1.9.2 has a very good CSV library which is useful for this stuff: http://www.ruby-doc.org/stdlib/libdoc/csv/rdoc/index.html
On earlier versions of Ruby you could use http://fastercsv.rubyforge.org/ (which essentially became CSV in 1.9.2)
You could do it manually by reading the file into a string and using .split(',') but I'd go with one of the libraries above.
Quick and dirty solution:
result = []
File.open("<path-to-file>","r") do |handle|
handle.each_line do |line|
result << line.split(",").strip
end
end # closes automatically when EOF reached
result.flatten!
result # => big array of values
Now you can iterate the result array and save the values to the database.
This simple file iteration doesn't take care for order or special fields, because it wasn't mentioned in the question.
Something easy to get you started:
IO.readlines("csv_file.txt", '').each do |line|
values = line.split(",").collect(&:strip)
# do something with the values?
end
Hope this helps.

Resources