In my rails controller I'm getting JSON posted to it that contains a person's work history. So they could have multiple jobs. The jobs are in a format like...
"values":
[
{
"title": "dummy position",
"company": "company 1",
"location": "Indianapolis, IN",
"description": "dummy job description",
"startDateMonth": "05",
"startDateYear": "2015",
"endDateMonth": "06",
"endDateYear": "2015"
},
{
"title": "dummy position 2",
"company": "company 2",
"location": "Indianapolis, IN",
"description": "dummy job description 2",
"startDateMonth": "02",
"startDateYear": "2015",
"endDateMonth": "05",
"endDateYear": "2015"
},
{
"title": "dummy position 3",
"company": "company 3",
"location": "Indianapolis, IN",
"description": "dummy job description 3",
"startDateMonth": "05",
"startDateYear": "2013",
"endDateMonth": "02",
"endDateYear": "2015"
}
]
I'm trying to add these to the params collection that I can later iterate through in an application class and add these work histories. I have the following code but it doesn't work - NoMethodError - undefined method `[]' for nil:NilClass: for this line params[:job_title][i] = jobs[i]['title']. It was just a guess, as I'm not great in ruby, so is there a way to make this work?
if job_count.numeric?
jobs = params['positions']['values']
for i in 0..job_count.to_i
params[:job_title][i] = jobs[i]['title']
params[:job_company_name][i] = jobs[i]['company']
params[:job_start_date][i] = jobs[i]['startDateMonth'] + '/1/' + jobs[i]['startDateYear']
params[:job_end_date][i] = jobs[i]['endDateMonth'] + '/1/' + jobs[i]['endDateYear']
params[:job_description][i] = jobs[i]['description']
end
end
EDIT: This ended up working.
i = 0
jobs.each do | k, work_history |
i = i + 1
params['job_title_' + i.to_s] = work_history['title']
params['job_company_name_' + i.to_s] = work_history['company']
params['job_start_date_' + i.to_s] = work_history['startDateMonth'] + '/1/' + work_history['startDateYear']
params['job_end_date_' + i.to_s] = work_history['endDateMonth'] + '/1/' + work_history['endDateYear']
params['job_description_' + i.to_s] = work_history['description']
end
You get this error because you're trying to do ["title"] on nil, as said, and jobs[i] is probably nil because job_count is wrong.
If you have 3 jobs, and job_count == 3, your for loop will do 0, 1, 2, 3. However, jobs[3] is rightfully nil.
Try replacing that for with for i in 0..(job_count.to_i - 1)
Related
I'm currently working on a simple hash loop, to manipulate some json data. Here's my Json data:
{
"polls": [
{ "id": 1, "question": "Pensez-vous utiliser le service de cordonnerie/pressing au moins 2 fois par mois ?" },
{ "id": 2, "question": "Avez-vous passé une bonne semaine ?" },
{ "id": 3, "question": "Le saviez-vous ? Il existe une journée d'accompagnement familial." }
],
"answers": [
{ "id": 1, "poll_id": 1, "value": true },
{ "id": 2, "poll_id": 3, "value": false },
{ "id": 3, "poll_id": 2, "value": 3 }
]
}
I want to have the poll_id value and the value from the answers hash. So here's what I code :
require 'json'
file = File.read('data.json')
datas = JSON.parse(file)
result = Hash.new
datas["answers"].each do |answer|
result["polls"] = {"id" => answer["poll_id"], "value" => answer["value"]}
end
polls_json = result.to_json
However, it returns me :
{
"polls": {
"id": 2,
"value": 3
}
}
Here's the output i am looking for :
{
"polls": [
{
"id": 1,
"value": true
},
{
"id": 2,
"value": 3
},
{
"id": 3,
"value": false
}
]
}
It seems that the value is not saved into my loop. I've tried different method but I still cannot find a solution .. Any suggestions?
You should be using reduce here, i.e.
datas["answers"].reduce({ polls: [] }) do |hash, data|
hash[:polls] << { id: data["poll_id"], value: data["value"] }
hash
end
This method iterates through the answers, making available the object supplied to reduce (in this case a hash with a :polls array) to which we pass each data hash.
I'd personally, um, reduce this a little further with the following, although it's at some cost to readability:
datas["answers"].reduce({ polls: [] }) do |hash, data|
hash.tap { |h| h[:polls] << { id: data["poll_id"], value: data["value"] } }
end
It's the cleanest method to achieve what you're looking for, using a built-for-purpose method.
Docs for reduce here: https://ruby-doc.org/core-2.1.0/Enumerable.html#method-i-reduce
(I'd also be inclined to update the variable names - data is already plural, so 'datas' is a little confusing to anyone else coming to your code.)
Edit: #max makes a great point re symbol / string keys from your data - keep that in mind if you attempt to apply this.
try the below:
require 'json'
file = File.read('data.json')
datas = JSON.parse(file)
result = Hash.new
poll_json = []
datas["answers"].each do |answer|
poll_json << {"id" => answer["poll_id"], "value" => answer["value"]}
end
p "json = "#{poll_json}"
{
polls: datas["answers"].map do |a|
{ id: a["poll_id"], value: a["value"] }
end
}
In general use .map to iterate through arrays and hashes and return new objects. .each should only be used when you are only concerned about the side effects (like in a view when you are outputting values).
require 'json'
json = JSON.parse(File.read('data.json'))
result = {
polls: json["answers"].map do |a|
{ id: a["poll_id"], value: a["value"] }
end
}
puts result.to_json
The output is:
{"polls":[{"id":1,"value":true},{"id":3,"value":false},{"id":2,"value":3}]}
How can I set the ID of the record or doc in the Firebase Database when I use the JSON import functionality?
When I import the file it creates sequential IDs (0, 1, 2 etc), but I would like to specify an ID so its easier to retrieve the record.
Below is my sample JSON data:
[
{
"GameID": 2234567890,
"GameName": "Team 3 vs Team 2",
"GameLocation": "Rink 4 Hockey Town",
"TypeOfGame": "Tournament Round Robin",
"HomeTeam": {
"Name": "Team 1",
"ImageUrl": "My Image URL 1",
"Level": "16AAA",
"Country": "USA"
}
}, {
"GameID": 1234567890,
"GameName": "Team 1 vs Team 2",
"GameLocation": "Rink 1 Hockey Town",
"TypeOfGame": "Tournament Round Robin",
"HomeTeam": {
"Name": "Team 1",
"ImageUrl": "My Image URL 1",
"Level": "16AAA",
"Country": "USA"
}
}
]
There is no way in which you can import a JSON file and generate a custom Id in the same time. You are getting (0, 1, 2 etc) as Ids because there is no unique identifier between those objects and Firebase sees all those records as a list, and therefor provides those ids for you.
To achieve what you want, you need to add that data programmatically using the push() function provided by Firebase for each record. This method generates a unique id which easy to be used in the future.
[ {
"1234567890" : {
"GameID" : 1234567890,
"GameLocation" : "Rink 1 Hockey Town",
"GameName" : "Team 1 vs Team 2",
"HomeTeam" : {
"Country" : "USA",
"ImageUrl" : "My Image URL 1",
"Level" : "16AAA",
"Name" : "Team 1"
},
"TypeOfGame" : "Tournament Round Robin"
},
"2234567890" : {
"GameID" : 2234567890,
"GameLocation" : "Rink 4 Hockey Town",
"GameName" : "Team 3 vs Team 2",
"HomeTeam" : {
"Country" : "USA",
"ImageUrl" : "My Image URL 1",
"Level" : "16AAA",
"Name" : "Team 1"
},
"TypeOfGame" : "Tournament Round Robin"
}
} ]
Can anyone help me with this problem?
So, here is the problem, I want to merge this query response:
#energy = Alert.where(["alert_type = ?", "Energy"]).last.as_json
#cost = Alert.where(["alert_type = ?", "Cost"]).last.as_json
Then I merge those object with:
#current_notif = #energy.merge(#cost)
But those just give me #cost object like this:
{
"alert_type": "Cost",
"value": 30000000,
"status": "Cost exceeds limit",
"created_at": "2017-06-03T15:31:21.156+07:00",
"updated_at": "2017-06-03T15:31:21.156+07:00",
"home_id": 2
}
Rather than a merged #energy + #cost like this:
{ {"alert_type": "Energy",
"value": 384455.813978742,
"status": "Energy too high",
"created_at": "2017-05-31T11:31:12.907+07:00",
"updated_at": "2017-05-31T11:31:12.907+07:00",
"home_id": 2 },
{
"alert_type": "Cost",
"value": 30000000,
"status": "Cost exceeds limit",
"created_at": "2017-06-03T15:31:21.156+07:00",
"updated_at": "2017-06-03T15:31:21.156+07:00",
"home_id": 2
}
}
If you want you could "join" both values, and then over that use as_json:
[#energy, #cost].as_json
# [{"alert_type": "Energy", ... }, {"alert_type": "Cost", ... }]
Or if you want you could use the IN expression, in order to deal with ActiveRecord instead having to customize the result this gives you:
Alert.where(alert_type: ['Energy', 'Cost']).as_json
# [{"alert_type": "Energy", ... }, {"alert_type": "Cost", ... }]
This is happening because that's how merge works.
hash = {:name => "Ade", :gender => "Male"}.merge(:name => "Bob")
puts hash # {:name=>"Bob", :gender=>"Male"}
Solution:
results = [ #energy, #cost ]
results.each do |result|
puts result['alert_type'] # Energy, Cost
end
I'm passing nested JSON into rails like so:
{
"product": {
"vendor": "Acme",
"categories":
{
"id": "3",
"method": "remove",
},
"categories":
{
"id": "4"
}
}
}
in order to update the category on a product. I am trying to iterate through the categories attribute in my products_controller so that I can add/remove the product to multiple categories at once:
def updateCategory
#product = Product.find(params[:id])
params[:product][:categories].each do |u|
#category = Category.find_by(id: params[:product][:categories][:id])
if params[:product][:categories][:method] == "remove"
#product.remove_from_category(#category)
else
#product.add_to_category(#category)
end
end
end
However, this only uses the second 'categories' ID in the update and doesn't iterate through both.
Example response JSON:
{
"product": {
"id": 20,
"title": "Heavy Duty Aluminum Chair",
"product_price": "47.47",
"vendor": "Acme",
"categories": [
{
"id": 4,
"title": "Category 4"
}
]
}
}
As you can see, it only added the category with ID = 4, and skipped over Category 3.
I'm fairly new to rails so I know I'm probably missing something obvious here. I've played around with the format of the JSON I'm passing in as well but it only made things worse.
You need to change your JSON structure. As you currently have it, the second "categories" reference will override the first one since you can only have 1 instance of a key. To get what you want, you should change it to:
{
"product": {
"vendor": "Acme",
"categories": [
{
"id": "3",
"method": "remove",
},
{
"id": "4"
}
]
}
}
You will also need to change your ruby code to look like:
def updateCategory
#product = Product.find(params[:id])
params[:product][:categories].each do |u|
#category = Category.find_by(id: u[:id])
if u[:method] == "remove"
#product.remove_from_category(#category)
else
#product.add_to_category(#category)
end
end
end
In Ruby, how can I traverse an arbitrary document retrieved from a collection using something like mongomapper? Let's say the document looks something like this:
mydocs = [{
"title": "my title",
"description": "hello world",
"comments": [{
"user": "me",
"text": "this"
}, {
"user": "him",
"text": "that"
}]
},
{
.....
}
]
def traverse(obj, level=0, name='root')
s = " "*level + name.to_s + ": "
if obj.is_a?(Array)
puts s
obj.each_with_index{ |child,idx| traverse(child,level+1,idx) }
elsif obj.is_a?(Hash)
puts s
obj.each{ |k,v| traverse(v,level+1,k) }
else
puts s + obj.inspect
end
end
traverse mydocs
After fetch a document from MongoMapper/Mongoid or even mongo-ruby-driver, it's like you generate an Hash.
So you can tranverse it like all hash in Ruby World