I'm trying to parse an array of hashes, grab a value from a specific key, and output to json with one predefined key for each value - and I'm stuck.
Array:
[{:object_id=>"jon-59",
:name=>"jon-59-gw (8db8fcae-055a-4b35-9f8f-739b68c0bd5d)",
:client_handle=>nil,
:extended_attributes=>nil,
:appliances_summary=>
{:vm_version=>"5.5.3",
:vm_build_info=>"5.5.3-2135647"},
:hypervisor_assist=>false,
:allowed_actions=>
{:string=>
["Change Log Level",
"Edit Dns",
"Edit Syslog"]},
:edge_assist_id=>"0"},
{:object_id=>"jon-60",
:name=>"jon-60-gw (d63ddc45-gd3c-40c3-9046-e7afa996934a)",
:client_handle=>nil,
:extended_attributes=>nil,
:appliances_summary=>
{:vm_version=>"5.5.3",
:vm_build_info=>"5.5.3-2168697"},
:hypervisor_assist=>false,
:allowed_actions=>
{:string=>
["Change Log Level",
"Edit Dns",
"Edit Syslog"]},
:edge_assist_id=>"0"}]
Desired Output
{
"data":[
{ "{#JONNAME}":"jon-59-gw" },
{ "{#JONNAME}":"jon-60-gw"},
]
}
Where I'm at:
def jon_discover
jon_summary.sort_by { |jon| jon[:object_id] }.each do |jon|
name = jon[:name].slice!(/\A\S*/)
my_hash = {'{#JONNAME}' => name}
puts JSON.generate(my_hash)
end
end
The above returns:
{ "{#JONNAME}":"jon-59-gw" }
{ "{#JONNAME}":"jon-60-gw" }
But I don't know where to take it from here, or if I'm on the right track. How can I get this into the desired output?
Thanks, cheers!
This is too complex:
my_hash = {"{#JONNAME}" => "#{name}"}
Keep it simple:
my_hash = {JONNAME => name}
the data is for Zabbix low level discovery
Then use single-quotes instead of double quotes for the key and use the bare name for the value:
my_hash = {'{#JONNAME}' => name}
so it's more apparent that {# is not a typo.
Instead of:
"jon-60-gw (d63ddc45-gd3c-40c3-9046-e7afa996934a)".slice!(/\A\S*/) # => "jon-60-gw"
Use:
"jon-60-gw (d63ddc45-gd3c-40c3-9046-e7afa996934a)".split.first # => "jon-60-gw"
Putting it all together:
require 'json'
ary = [
{
:object_id => "jon-59",
:name => "jon-59-gw (8db8fcae-055a-4b35-9f8f-739b68c0bd5d)",
:client_handle => nil,
:extended_attributes => nil,
:appliances_summary =>
{
:vm_version => "5.5.3",
:vm_build_info => "5.5.3-2135647"
},
:hypervisor_assist => false,
:allowed_actions => {
:string => ["Change Log Level", "Edit Dns", "Edit Syslog"]
},
:edge_assist_id => "0"
},
{
:object_id => "jon-60",
:name => "jon-60-gw (d63ddc45-gd3c-40c3-9046-e7afa996934a)",
:client_handle => nil,
:extended_attributes => nil,
:appliances_summary => {
:vm_version => "5.5.3",
:vm_build_info => "5.5.3-2168697"
},
:hypervisor_assist => false,
:allowed_actions => {
:string => ["Change Log Level", "Edit Dns", "Edit Syslog"]
},
:edge_assist_id => "0"
}
]
Here's how to walk through the data:
data = ary.map{ |hash|
{
'{#JONNAME}' => hash[:name].split.first
}
}
Here's how to generate the JSON:
puts JSON[{'data' => data}]
# >> {"data":[{"{#JONNAME}":"jon-59-gw"},{"{#JONNAME}":"jon-60-gw"}]}
If you need it sorted:
puts JSON[{'data' => data.sort_by{ |s| s['{#JONNAME}'] }}]
# >> {"data":[{"{#JONNAME}":"jon-59-gw"},{"{#JONNAME}":"jon-60-gw"}]}
Related
I am new here and i hope that I'm doing everything right.
I also searched the Forum and with Googel, but I didn't find the answer. (Or I did not notice that the solution lies before my eyes. Then I'm sorry >.< .)
i have a problem and i dont exactly know what i am doing wrong at the moment.
I make a API request and get a big JSON back. It looks somehow like that:
"apps": [
{
"title": "XX",
... many more data
},
{
"title": "XX",
... many more data
},
{
"title": "XX",
... many more data
}
... and so on
]
After that i want to create a hash with the data i need, for example it should look like:
{
"APP_0" => {"Title"=>"Name1", "ID"=>"1234", "OS"=>"os"}
"APP_1" => {"Title"=>"Name2", "ID"=>"5678", "OS"=>"os"}
}
but the values in the hash that i create with my code looks like:
"APP_1", {"Title"=>"Name2", "ID"=>"5678", "OS"=>"os"}
dont now if this is a valid hash? And after that i want to iterate through the Hash and just output the ID. But I get an error (TypeError). What am i doing wrong?
require 'json'
require 'net/http'
require 'uri'
require 'httparty'
response = HTTParty.get('https://xxx/api/2/app', {
headers: {"X-Toke" => "xyz"},
})
all_apps_parse = JSON.parse(response.body)
all_apps = Hash.new
all_apps_parse["apps"].each_with_index do |app, i|
all_apps["APP_#{i}"] = {'Title' => app["title"],
'ID' => app["id"],
'OS' => app["platform"]}
end
all_apps.each_with_index do |app, i|
app_id = app["App_#{i}"]["id"]
p app_id
end
I hope someone can understand the problem and can help me :-). Thanks in advance.
Assuming the data looks something like this:
all_apps_parse = { "apps" => [
{
"title" => "Name1",
"id" => 1234,
"platform" => "os"
},
{
"title" => "Name2",
"id" => 5678,
"platform" => "os"
},
{
"title" => "Name3",
"id" => 1111,
"platform" => "windows"
}]
}
and with a little idea of what you want to achieve, here is my solution:
all_apps = Hash.new
all_apps_parse["apps"].each_with_index do |app, i|
all_apps["APP_#{i}"] = { 'Title' => app["title"],
'ID' => app["id"],
'OS' => app["platform"] }
end
all_apps
=> {"APP_0"=>{"Title"=>"Name1", "ID"=>1234, "OS"=>"os"}, "APP_1"=>{"Title"=>"Name2", "ID"=>5678, "OS"=>"os"}, "APP_2"=>{"Title"=>"Name3", "ID"=>1111, "OS"=>"windows"}}
all_apps.each do |key, value|
puts key # => e.g. "APP_0"
puts value['ID'] # => e.g. 1234
end
# Prints
APP_0
1234
APP_1
5678
APP_2
1111
If I have a hash like the one below, and I want to loop over the second level keys.
Why does this fail?
hash["Element"].each do |id|
h[id] = hash[id]["Name"]
end
hash
{
"Element" => {
"499723" => {
"Name" => "A",
},
"499725" => {
"Name" => "B",
},
}
It fails because using .each on a Hash yields the tuple of key and value.
hash = {
"Element" => {
"499723" => {
"Name" => "A",
},
"499725" => {
"Name" => "B",
},
}
}
hash["Element"].each do |id|
p id
end
["499723", {"Name"=>"A"}]
["499725", {"Name"=>"B"}]
Therefore, you need to use
hash["Element"].each do |id, value|
# ...
end
If you don't need the value
hash["Element"].each do |id, _|
# ...
end
However, you can keep the value and access it directly
hash["Element"].each do |id, value|
h[id] = value["Name"]
end
A simple solution in your case is to use Enumberable#each_with_object in combination with the previous information:
hash["Element"].each_with_object({}) do |(id, value), acc|
acc[id] = value["Name"]
end
# => {"499723"=>"A", "499725"=>"B"}
hash["Element"].each.with_object({}) do |(id, subhash), result|
result[id] = subhash["Name"]
end
Here is the hashes that will be processed:
{
"flatiron school bk" => {
:location => "NYC"
},
"flatiron school" => {
:location => "NYC"
},
"dev boot camp" => {
:location => "SF"
},
"dev boot camp chicago" => {
:location => "Chicago"
},
"general assembly" => {
:location => "NYC"
},
"Hack Reactor" => {
:location => "SF"
}
}
I need to organize these hashes by location, like this:
{ "NYC"=>["flatiron school bk", "flatiron school", "general assembly"],
"SF"=>["dev boot camp", "Hack Reactor"],
"Chicago"=>["dev boot camp chicago"]}
}
You can use each_with_object to combine in into new hash:
hash.each_with_object({}) do |(name, data), res|
(res[data[:location]] ||= []) << name
end
Explanation:
each_with_object
Iterates the given block for each element with an arbitrary object given, and returns the initially given object.
In this case name and data is key and value of each element in given hash.
In (res[data[:location]] ||= []) << name you get location, create array in result hash for given location (if it doesn't exist), then put key of input hash to it.
What method is missing in my program to make the 2 worded states work like New Jersey and South Carolina and etc. I know by putting an underscore in between words would be easier but the user would also need to include underscore in their input which is not so cool. I would really appreciate any help. Thanks!
us_capitals = {
"Alabama"=>"Montgomery",
"Alaska" => "Juneau",
"Arizona" => "Phoenix",
"Arkansas" => "Little Rock",
"California" => "Sacramento",
"Colorado" => "Denver",
"Connecticut" => "Hartford",
"Delaware" => "Dover",
"Florida" => "Tallahassee",
"Georgia" => "Atlanta",
"Hawaii" => "Honolulu",
"Idaho" => "Boise",
"Illinois" => "Springfield",
"Indiana" => "Indianapolis",
"Iowa" => "Des Moines",
"Kansas" => "Topeka",
"Kentucky" => "Frankfort",
"Louisiana" => "Baton Rouge",
"Maine" => "Augusta",
"Maryland" => "Annapolis",
"Massachusetts" => "Boston",
"Michigan" => "Lansing",
"Minnesota" => "Saint Paul",
"Mississippi" => "Jackson",
"Missouri" => "Jefferson",
"Montana" => "Helena",
"Nebraska" => "Lincoln",
"Nevada" => "Carson City",
"New Hampshire" => "Concord",
"New Jersey" => "Trenton",
"New Mexico" => "Santa Fe",
"New York" => "Albany",
"North Carolina" => "Raleigh",
"North Dakota" => "Bismarck",
"Ohio" => "Columbus",
"Oklahoma" => "Oklahoma City",
"Oregon" => "Salem",
"Pennsylvania" => "Harrisburg",
"Rhode Island" => "Providence",
"South Carolina" => "Columbia",
"South Dakota" => "Pierre",
"Tennessee" => "Nashville",
"Texas" => "Austin",
"Utah" => "Salt Lake City",
"Vermont" => "Montpelier",
"Virginia" => "Richmond",
"Washington" => "Olympia",
"West Virginia" => "Charleston",
"Wisconsin" => "Madison",
"Wyoming" => "Cheyenne"
}
loop do
puts "Enter a state to lookup it's capital:"
state = gets.chomp.capitalize
puts us_capitals[state]
end
Capitalize only formats the first letter in the string, thus it is not working with strings that contain multiple words. You can try to split your strings by space, capitalize each word and then join it back:
state = gets.chomp.split(' ').map(&:capitalize).join(' ')
Demonstration
If you were using Rails, you could just use titleize:
gets.chomp.titleize
Suppose gets returns "west virginIa\n". Then
r = /[[:alpha:]]+/
state = gets.chomp.squeeze(' ').gsub(r) { |s| s.capitalize }
#=> "west virginIa\n".chomp.squeeze(' ').gsub(r) { |s| s.capitalize }
#=> "west virginIa".squeeze(' ').gsub(r) { |s| s.capitalize }
#=> "west virginia".gsub(r) { |s| s.capitalize }
#=> "West Virginia"
us_capitals[state]
#=> "Charleston"
If gets returns "West Virgin1a", we obtain
state = gets.chomp.squeeze(' ').gsub(r) { |s| s.capitalize }
#=> "West Virgin1a"
us_capitals[state]
#=> nil
Recall that String#capitalize converts the first letter of a string to a capital letter (if it is a letter), but it also converts all other letters in the string to lower case.
It seems like the simplest solution would be to make the hash keys lowercase and then downcase the user input.
us_capitals = {
# ...
"nebraska" => "Lincoln",
"nevada" => "Carson City",
"new hampshire" => "Concord",
"new jersey" => "Trenton",
# ...
}
loop do
puts "Enter a state to lookup it's capital:"
state = gets.chomp.downcase
puts us_capitals[state]
end
Can you add a helper method to convert user input?
#state = "new jersey"
state.gsub!(/\b[a-z]/, &:capitalize)
#state = "New Jersey"
Credit to Jordan, in the comments
I'm using Grape on Padrino to make a test API for my mobile app.
How can I specify the type of my JSON object?
Here is how i do it, but every returned value is a String:
module Acme
module Api
class Ping < Grape::API
format :json
get '/user/112132a08s245c/availability_list' do
{
"availability_list"=> [
{
:type=> "OOO",
:from_date=> "21-12-2004",
:to_date=> "21-23-2007",
:all_day=> "false"
},
{
:type=> "WFH",
:from_date=> "21-12-2004",
:to_date=> "21-23-2007",
:all_day=> "false"
}
]
}
end
get '/user/112132a08s245c/issues' do
{
"issues"=> [
{
:issure_id=> "1ab300co221",
:title=> "No water",
:description=> "No water in kitchen",
:severity=> "low",
"location" => {
:lat => "37.4224764",
:lng => "-122.0842499"
}
},
{
:issure_id=> "1ab300co222",
:title=> "No fire",
:description=> "No fire in kitchen",
:severity=> "low",
"location" => {
:lat => "37.4224764",
:lng => "-122.0842499"
}
}
]
}
end
end
end
Meditate on this:
require 'json'
foo = {'a' => 1}
foo.class # => Hash
str = JSON[foo] # => "{\"a\":1}"
str.class # => String
bar = JSON[str] # => {"a"=>1}
bar.class # => Hash
You need to read the JSON spec. JSON serializes data into a string because objects can't be transferred between disparate languages. When it sees an object the parser serializes it into a string. When the incoming string is received and passed on to the parser, it knows it has to convert the string back to an object.