Related
the hash I have is the following:
aoh=[
{ "name": "Vesper",
"glass": "martini",
"category": "Before Dinner Cocktail",
"ingredients": [
{ "unit": "cl",
"amount": 6,
"ingredient": "Gin" },
{ "unit": "cl",
"amount": 1.5,
"ingredient": "Vodka" },
{ "unit": "cl",
"amount": 0.75,
"ingredient": "Lillet Blonde" }
],
"garnish": "Lemon twist",
"preparation": "Shake and strain into a chilled cocktail glass." },
{ "name": "Bacardi",
"glass": "martini",
"category": "Before Dinner Cocktail",
"ingredients": [
{ "unit": "cl",
"amount": 4.5,
"ingredient": "White rum",
"label": "Bacardi White Rum" },
{ "unit": "cl",
"amount": 2,
"ingredient": "Lime juice" },
{ "unit": "cl",
"amount": 1,
"ingredient": "Syrup",
"label": "Grenadine" }
],
"preparation": "Shake with ice cubes. Strain into chilled cocktail glass." }]
How can I iterate through this to get JUST the ingredient (without returning name,glass,category,etc.)? I also need the same iteration for amount but I assume that will look just like the iteration for ingredient. Sorry for the dumb question, I'm new to ruby and have attempted this for hours now.
You have an array of two elements in your example. Those two elements are hashes with key/value pairs. You can loop through the array with the #each method and access the values that the :"ingredients" keys store like this:
aoh.each do |hash|
hash[:ingredients]
end
The :ingredients keys each store another array of hashes. An example hash is:
{ "unit": "cl",
"amount": 6,
"ingredient": "Gin" }
You can then access the value under the :ingredient key by doing hash[:ingredient]. The final result looks something like this:
aoh.each do |array_element|
array_element[:ingredients].each do |ingredient|
ingredient[:ingredient]
end
end
This currently only iterates through the arrays and hashes. If you want to also print the result you can do this:
aoh.each do |array_element|
array_element[:ingredients].each do |ingredient|
puts ingredient[:ingredient]
end
end
#=> Gin
# Vodka
# Lillet Blonde
# White rum
# Lime juice
# Syrup
If you want to get a modified array, you can use #map (or #flat_map). You can also get the amount with the value like this:
aoh.flat_map do |array_element|
array_element[:ingredients].map do |ingredient|
[[ingredient[:ingredient], ingredient[:amount]]
end
end
#=> [["Gin", 6], ["Vodka", 1.5], ["Lillet Blonde", 0.75], ["White rum", 4.5], ["Lime juice", 2], ["Syrup", 1]]
>aoh.collect { |i| i[:ingredients].collect { |g| puts g[:ingredient] } }
Gin
Vodka
Lillet Blonde
White rum
Lime juice
Syrup
I would suggest the following.
aoh=[
{ "name": "Vesper",
"ingredients": [
{ "unit": "cl", "ingredient": "Gin" },
{ "unit": "cl", "ingredient": "Vodka" }
],
"garnish": "Lemon twist"
},
{ "name": "Bacardi",
"ingredients": [
{ "unit": "cl", "ingredient": "White rum" },
{ "unit": "cl", "ingredient": "Lime juice" }
],
}
]
aoh.each_with_object({}) { |g,h| h[g[:name]] =
g[:ingredients].map { |f| f[:ingredient] } }
#=> {"Vesper"=>["Gin", "Vodka"], "Bacardi"=>["White rum", "Lime juice"]}
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
I am writing a ruby script to accept an input file that is a CSV. I want to make a pretty JSON file. I feel that I am so close but I can't seem to get there. My original project was in JS but the requirements have changed to make it a Ruby file.
My input file looks like this
item id,description,price,cost,price_type,quantity_on_hand,size_1_name,size_1_price,size_2_name,size_2_price,size_3_name,size_3_price
one thing to note is that some of the values in the CSV file maybe missing because it doesn't exist.
require 'csv'
require 'json'
def is_int(str)
return !!(str =~ /^[-+]?[1-9]([0-9]*)?$/)
end
lines = CSV.open(ARGV[0],{:col_sep => ","}).readlines
# remove first entry of the lines array
keys = lines.shift
lines.each do |values|
# convert the line into a hash and transform string into int
#hash=Hash[keys.zip(values.map{|val| is_int(val) ? val.to_i : val}) ]
hash = keys.zip(values.map{ val})
# Write a file with the hash results
File.open("#{hash['NAME']}.json", "w") do |f|
f.write JSON.pretty_generate [hash]
end
end
The output I am trying to get is
[
{
id: 111010,
description: 'Coffee',
price: 1.25,
cost: 0.80,
price_type: 'system',
quantity_on_hand: 100000,
modifiers: [
{
name: 'Small',
price: -0.25
},{
name: 'Medium',
price: 0.00
},{
name: 'Large',
price: 0.30
}
]
the version of Ruby I'm using is 2.0.0p481
Error was
usr/lib/ruby/2.0.0/csv.rb:1254:in `initialize': no implicit conversion of nil into String (TypeError)
from /usr/lib/ruby/2.0.0/csv.rb:1254:in `open'
from /usr/lib/ruby/2.0.0/csv.rb:1254:in `open'
from stockimporter.rb:8:in `<main>'
csv.csv:
id,description,price,cost,price_type,quantity_on_hand,size_1_name,size_1_price,size_2_name,size_2_price,size_3_name,size_3_price
111010,Coffee,1.25,0.80,system,10000,Small,-0.25,Medium,0.00,Large,0.30
111011,Tea,1.00,0.50,system,100,Small,-0.10,Medium,0.00,Large,0.10
111012,MissingInfo,1.00,0.50,,100,,,Medium,,Large,0.10
...
require 'csv'
require 'pp'
require 'json'
csv_options = {
headers: true, #skip first line of csv file
converters: [:numeric] #convert strings that look like integers or floats to integers or floats
}
results = []
CSV.foreach('csv.csv', csv_options) do |row|
record = Hash[row.take(6)]
modifiers = [
{
name: row["size_1_name"],
price: row["size_1_price"]
},
{
name: row["size_2_name"],
price: row["size_2_price"],
},
{
name: row["size_3_name"],
price: row["size_3_price"]
}
]
record['modifiers'] = modifiers
results << record
end
pp results
--output:--
[{"id"=>111010,
"description"=>"Coffee",
"price"=>1.25,
"cost"=>0.8,
"price_type"=>"system",
"quantity_on_hand"=>10000,
"modifiers"=>
[{:name=>"Small", :price=>-0.25},
{:name=>"Medium", :price=>0.0},
{:name=>"Large", :price=>0.3}]},
{"id"=>111011,
"description"=>"Tea",
"price"=>1.0,
"cost"=>0.5,
"price_type"=>"system",
"quantity_on_hand"=>100,
"modifiers"=>
[{:name=>"Small", :price=>-0.1},
{:name=>"Medium", :price=>0.0},
{:name=>"Large", :price=>0.1}]},
{"id"=>111012,
"description"=>"MissingInfo",
"price"=>1.0,
"cost"=>0.5,
"price_type"=>nil,
"quantity_on_hand"=>100,
"modifiers"=>
[{:name=>nil, :price=>nil},
{:name=>"Medium", :price=>nil},
{:name=>"Large", :price=>0.1}]}]
json = JSON.pretty_generate(results)
puts json
--output:--
[
{
"id": 111010,
"description": "Coffee",
"price": 1.25,
"cost": 0.8,
"price_type": "system",
"quantity_on_hand": 10000,
"modifiers": [
{
"name": "Small",
"price": -0.25
},
{
"name": "Medium",
"price": 0.0
},
{
"name": "Large",
"price": 0.3
}
]
},
{
"id": 111011,
"description": "Tea",
"price": 1.0,
"cost": 0.5,
"price_type": "system",
"quantity_on_hand": 100,
"modifiers": [
{
"name": "Small",
"price": -0.1
},
{
"name": "Medium",
"price": 0.0
},
{
"name": "Large",
"price": 0.1
}
]
},
{
"id": 111012,
"description": "MissingInfo",
"price": 1.0,
"cost": 0.5,
"price_type": null,
"quantity_on_hand": 100,
"modifiers": [
{
"name": null,
"price": null
},
{
"name": "Medium",
"price": null
},
{
"name": "Large",
"price": 0.1
}
]
}
]
You could also do it like this:
CSV.foreach('csv.csv', csv_options) do |row|
record = Hash[row.take(6)]
price_adjustments = row.drop(6)
# [["size_1_name", "Small"], ["size_1_price", -0.25]]
# |---------------------------------------------------|
# ^
# |
modifiers = price_adjustments.each_slice(2).map do |size_price|
size_price.first[0] = 'name'
size_price.last[0] = 'price'
Hash[size_price]
end
p modifiers #e.g. [{"name"=>"Small", "price"=>-0.25}, {"name"=>"Medium", "price"=>0.0}, {"name"=>"Large", "price"=>0.3}]
record['modifiers'] = modifiers
results << record
end
pp results
I'm really new to working with JSON in Ruby, and am having a hard time figuring out why a script I'm running is generating blank lines as a result. Let's say that I'm parsing a file where one element is not always present. I want to check if that element exists, and depending on the result name variables in a specific way. Here is what I've been trying
require 'rubygems'
require 'json'
file = File.open("/path/to/file.json", encoding: 'UTF-8')
json = file.read
data = JSON.parse(json)
if data["snapshots"][-1]["responses"][2].nil?
item = something
elseif
item = something else
end
puts item
This is not generating an error, but is just producing a blank line. I'm sure I'm doing something obvious wrong, but would appreciate any help. Thanks!
Your main problem is that you had elseif instead of elsif
Full code using the JSON you provided below:
require 'json'
json = <<EOS
{
"snapshots": [
{
"steps": 10,
"responses": [
{
"tokens": [
"Answer"
],
"questionPrompt": "Question1?"
},
{
"tokens": [
"Answer"
],
"questionPrompt": "Question2?"
},
{
"locationResponse": {
"location": {
"speed": 0,
"timestamp": "2014-04-20T17: 28: 37-0400",
"longitude": "-xx.xxxxxxx",
"latitude": "xx.xxxxxx",
"verticalAccuracy": 3,
"course": 0,
"horizontalAccuracy": 5
},
"text": "Response"
},
"questionPrompt": "Question3?"
},
{
"tokens": [
"Answer"
],
"questionPrompt": "Question4?"
}
],
"battery": 0.75,
"sectionIdentifier": "1-2014-5-7",
"audio": {
"avg": -49.84988,
"peak": -39.73056
},
"background": 0,
"date": "2014-05-07T23: 20: 57-0400",
"location": {
"speed": -1,
"placemark": {
"subAdministrativeArea": "County",
"subLocality": "CityName",
"thoroughfare": "Street",
"administrativeArea": "xx",
"subThoroughfare": "xxx",
"postalCode": "xxxxx",
"region": "<+xx.xxxxxx",
"radius": 28.13,
"country": "UnitedStates",
"locality": "CityName",
"name": "Address"
},
"timestamp": "2014-05-07T23: 20: 58-0400",
"longitude": "-xx.xxxxxxx",
"latitude": "xx.xxxxxxx",
"verticalAccuracy": 10,
"course": 0,
"horizontalAccuracy": 65
},
"dwellStatus": 0,
"weather": {
"relativeHumidity": "68%",
"visibilityKM": 16.1,
"tempC": 13.3,
"precipTodayIn": 0,
"windKPH": 0,
"latitude": 40.813984,
"windDegrees": 159,
"stationID": "xxxxxxxx",
"visibilityMi": 10,
"pressureIn": 30.2,
"pressureMb": 1023,
"feelslikeF": 55.9,
"windGustKPH": 12.4,
"longitude": -77.895775,
"feelslikeC": 13.3,
"precipTodayMetric": 0,
"tempF": 55.9,
"windDirection": "SSE",
"dewpointC": 8,
"uv": 0,
"weather": "Overcast",
"windGustMPH": 7.7,
"windMPH": 0
},
"connection": 1,
"sync": 0,
"reportImpetus": 0,
"draft": 0
}
]
}
EOS
data = JSON.parse(json)
if data["snapshots"][-1]["responses"][2].nil?
item = "was nil"
elsif # THIS WAS elseif before
item = "NOT nil"
end
puts item
I have two json files that I'm trying to merge. The JSONs have different formatting (see below). I'd like to merge records, so [0] from file one and [0] from file two would become one record [0] in the new merged file.
The first JSON (file_a.json), appears like so:
{
"query": {
"count": 4,
"created": "2012-11-21T23:07:00Z",
"lang": "en-US",
"results": {
"quote": [
{
"Name": "Bill",
"Age": "46",
"Number": "3.55"
},
{
"Name": "Jane",
"Age": "33",
"Number": nil
},
{
"Name": "Jack",
"Age": "55",
"Number": nil
},
{
"Name": "Xavier",
"Age": nil,
"Number": "153353535"
}
]
}
}
}
The second JSON (file_b.json) appears like so:
[
{
"Number2": 25253,
"Number3": 435574,
"NAME": "Bill"
},
{
"Number2": 345353,
"Number3": 5566,
"NAME": "Jane"
},
{
"Number2": 56756,
"Number3": 232435,
"NAME": "Jack"
},
{
"Number2": 7457,
"Number3": 45425,
"NAME": "Xavier"
}
]
None of the keys are the same in both JSONs (well, actually "Name" is a key in both, but in the first the key is "Name" and in the second its "NAME" - just so I can check that the merge works correctly - so I want "Name" and "NAME" in the final JSON), the first record in the first file matches with the first record in the second file, and so on.
So far, I tried merging like this:
merged = %w[a b].inject([]) { |m,f| m << JSON.parse(File.read("file_#{f}.json")) }.flatten
But this of course merged them, but not how I wanted them merged (they are merged sucessively, and because of the different formatting, it gets quite ugly).
I also tried merging like this:
a = JSON.parse(File.read("file_a.json"))
b = JSON.parse(File.read("file_b.json"))
merged = a.zip(b)
Came closer but still not correct and the formatting was still horrendous.
In the end, what I want is this (formatting of second JSON - headers from first JSON can be junked):
[
{
"Name": "Bill",
"Age": 46,
"Number": 3.55,
"Number2": 25253,
"Number3": 435574,
"NAME": "Bill"
},
{
"Name": "Jane",
"Age": 33,
"Number": nil,
"Number2": 345353,
"Number3": 5566,
"NAME": "Jane"
},
{
"Name": "Jack",
"Age": 55,
"Number": nil,
"Number2": 56756,
"Number3": 232435,
"NAME": "Jack"
},
{
"Name": "Xavier",
"Age": nil,
"Number": 153353535,
"Number2": 7457,
"Number3": 45425,
"NAME": "Xavier"
}
]
Any help is appreciated. Thanks a lot.
Hеllo, seems format changed from last time :)
UPDATE: more readable version that also convert corresponding values to integers/floats:
require 'json'
require 'ap'
a = JSON.parse(File.read('./a.json'))['query']['results']['quote'] rescue []
b = JSON.parse(File.read('./b.json'))
final = []
a.each_with_index do |ah,i|
unless bh = b[i]
bh = {}
puts "seems b has no #{i} key, merging skipped"
end
final << ah.merge(bh).inject({}) do |f, (k,v)|
if v.is_a?(String)
if v =~ /\A\d+\.\d+\Z/
v = v.to_f
elsif v =~ /\A\d+\Z/
v = v.to_i
end
end
f.update k => v
end
end
ap final
will display:
[
[0] {
"Name" => "Bill",
"Age" => 46,
"Number" => 3.55,
"Number2" => 25253,
"Number3" => 435574,
"NAME" => "Bill"
},
[1] {
"Name" => "Jane",
"Age" => 33,
"Number" => nil,
"Number2" => 345353,
"Number3" => 5566,
"NAME" => "Jane"
},
[2] {
"Name" => "Jack",
"Age" => 55,
"Number" => nil,
"Number2" => 56756,
"Number3" => 232435,
"NAME" => "Jack"
},
[3] {
"Name" => "Xavier",
"Age" => nil,
"Number" => 153353535,
"Number2" => 7457,
"Number3" => 45425,
"NAME" => "Xavier"
}
]
Here is a working demo
Btw, your json is a bit wrong in both files.
See the fixed versions here and here