Rails: How to update the hash in an array - ruby-on-rails

How to update a hash from an array?
Sample Data
form_fields = [
{
key: '1',
properties: null
},
{
key: '2',
properties: {"options"=>[{"label"=>"Europe", "value"=>"europe"}, {"label"=>"North America", "type"=>"groupstart"}, {"label"=>"Canada", "value"=>"canada"}, {"label"=>"USA", "value"=>"usa"}, {"label"=>"", "type"=>"groupend"}]}
}
]
Code I have so far
form_fields = form_fields.map {
|field| {
field.properties = field.properties ? JSON.parse(field.properties) : {}
}
I built this based on some other questions I came across such as this one How do I modify an array while I am iterating over it in Ruby?

The syntax for the map is very close to what you already have, the correct syntax would be like this:
form_fields = form_fields.map {
|field|
...
}
To access a object in the Hash structure you would use the symbol, or string as the selector, like this:
field[:properties]
field[:properties]["options"]
The code you have in your map, does not really make sense. The object stored in the code you provided is already ruby code, the hash keys is just strings instead of symbols.
You do also not want to update your form_fields variable to the result of your map, since that will overwrite your array, and only keep the last element in the array.
I believe what you want is something like this:
form_fields.map { |field|
field[:properties] = field[:properties] ? field[:properties] : {}
}
That will turn your array from:
[{:key=>"1", :properties=>nil}, {:key=>"2", :properties=>{ ... }}]
to:
[{:key=>"1", :properties=>{}}, {:key=>"2", :properties=>{ ... }}]

Related

Re-ordering hash items to have some appear at the end

I'm returning a hash of form inputs, which at the moment looks like:
hash = {
"body"=>:text,
"button_text"=>:string,
"category"=>:integer,
"dashboard"=>:boolean,
"feature"=>:integer,
"featured_from"=>:datetime,
"featured_to"=>:datetime,
"global"=>:boolean,
"hyperlink"=>:string,
"jobs"=>:boolean,
"labs"=>:boolean,
"leaderboard"=>:boolean,
"management"=>:boolean,
"news"=>:boolean,
"objectives"=>:boolean,
"only_for_enterprise_users"=>:boolean,
"published_at"=>:datetime,
"title"=>:string,
"workflow_state"=>:string
}
I need to place the following keys at the end:
["dashboard", "jobs", "labs", "management", "news", "objectives", "global"]
Which will leave me with:
{
"body"=>:text,
"button_text"=>:string,
"category"=>:integer,
"feature"=>:integer,
"featured_from"=>:datetime,
"featured_to"=>:datetime,
"hyperlink"=>:string,
"only_for_enterprise_users"=>:boolean,
"published_at"=>:datetime,
"title"=>:string,
"workflow_state"=>:string,
"dashboard"=>:boolean,
"jobs"=>:boolean,
"labs"=>:boolean,
"leaderboard"=>:boolean,
"management"=>:boolean,
"news"=>:boolean,
"objectives"=>:boolean,
"global"=>:boolean
}
All the links I've found relate to transforming keys / values without re-ordering, and outside of manually deleting each key and then reinserting I can't see another way of getting my desired output.
Is there an easy way to achieve what I need?
Thanks in advance
You can try following,
(hash.keys - end_keys + end_keys).map { |key| [key, hash[key]] }.to_h
hash = { "body"=>:text, "button_text"=>:string, "category"=>:integer,
"dashboard"=>:boolean, "feature"=>:integer }
enders = ["button_text", "dashboard"]
hash.dup.tap { |h| enders.each { |k| h.update(k=>h.delete(k)) } }
See Object#tap, Hash#update (aka merge!) and Hash#delete.
dup may of course be removed if hash can be mutated, which may be reasonable as only the keys are being reordered.

Loop specific values from deep nested hash and array

I'm new to ruby and am having difficulty with looping through deep nested hashes and arrays.
Say I have the following JSON:
{
"Resume":{
.... data ....
},
"StructuredXMLResume":{
"ContactInfo":{
.... data ....
}
]
},
"EmploymentHistory":{
"EmployerOrg":[
{
"EmployerOrgName":"ABC Corp.",
"PositionHistory":[
{
.... data ....
]
},
{
"EmployerOrgName":"National Geo.",
"PositionHistory":[
{
.... data ....
}
]
}
]
}
]
}
.
resume.["Resume"]["StructuredXMLResume"]["EmploymentHistory"]["EmployerOrg"][0]["EmployerOrgName"]
gives me ABC Corp. and
resume.["Resume"]["StructuredXMLResume"]["EmploymentHistory"]["EmployerOrg"][1]["EmployerOrgName"]
gives me National Geo.
How do I loop to print each EmployerOrgName?
Use:
resume.["Resume"]["StructuredXMLResume"]["EmploymentHistory"]["EmployerOrg"].each do |employer_org|
puts employer_org["EmployerOrgName"] # or whatever you want to do with the employer_org hash
end
You can use a enumerator, like each, on any "thing" that either is or behaves like an array/hash object, so in this case you would want to:
all_employerorgs = resume.["Resume"]["StructuredXMLResume"]["EmploymentHistory"]["EmployerOrg"]
# which is an array of hashes that then you can iterate through
# you could iterate directly on that but for readibility
# I would always assign it to a var
all_employerorgs.each do |employerorg|
puts employerorg['EmployerOrgName']
end

Rails app: I need to build a json object from params inside a loop

I need to build a json object inside a loop using params.
My params look like this...
params[:answers]
returns => {"1"=>"answer1", "2"=>"answer2"}
The keys in this json object are the id's of the survey question.
So I planed to loop through the keys to build the json object like this...
def build_answersheet_json(params[:answers], params[:survey_id])
params[:answers].keys.each do |question_id|
current_question = question_id
current_answer = params[:answers][question_id]
end
end
Since im using "t.json" in my migration to save json to postgres, I wanted to use the extracted question_id and answer to build a json object that looks something like this...
{
survey_id: '1',
answers: {
question: [{
question_id: 1,
answer: 'answer1'
}, {
question_id: 2,
answer: 'answer2'
}]
}
}
Ive been trying to do this using a method that looks somthing like this...
build_answersheet_json(params[:answers], params[:survey_id])
Ive tried JSON.parse() and Ive tried to just logically work through it but I cant seem to figure this out.
Any help is appreciated.
Maybe you can try something like that:
/* fake params (to test) */
params = {
survey_id: '1',
answers: {
"1"=>"answer1",
"2"=>"answer2",
"3"=>"answer3",
"4"=>"answer4"
}
}
def build_answersheet_json(answers, survey_id)
{
survey_id: survey_id,
answers: answers.map { |k,v| { question_id: k.to_i, answer: v } }
}
end
survey = build_answersheet_json(params[:answers], params[:survey_id])
puts survey.class
#Hash
puts survey.to_json
# formated JSON string:
# {
# "survey_id":"1",
# "answers":[
# {"question_id":1,"answer":"answer1"},
# {"question_id":2,"answer":"answer2"},
# {"question_id":3,"answer":"answer3"},
# {"question_id":4,"answer":"answer4"}
# ]
# }
In order to save to a t.json postgress column type, just pass the Hash survey object, like that:
YourModel.create(survey: survey)
Source: http://edgeguides.rubyonrails.org/active_record_postgresql.html
Try
{
survey: ¯\_༼◉ل͟◉༽_/¯,
}
Json may not be parsed if json have construction like this:
survey = {
}
Json may not contain = and assignment
Check real variables values with puts varname.inspect near at code lines where you meet unexpected behaviour.

Rails 4 and returning JSON response: how to correctly append extra data?

I have a SQL query returns some data, here is some sample output:
[
{
"AccountCode": "111123456",
"AccountID": 123456,
"BalanceCurrent": "-8.0",
"Phone": "123456888",
}
]
This is a Hash with an array. There are times when there will be multiple hashes within the array. Just one in this example though.
As stated, this data comes directly from the database.
I have a lookup_phone method in my Customer model that runs the SQL query and then executed in the customer_controller.rb file like so:
customer_phone = Customer.lookup_phone(params[:Phone])
Now, I need to append some extra data to these hash(es) that do not come from the database, like so:
data = [
:match_found => true,
:transfer_flag => false,
:confirm_id => 2
]
This data variable needs to be WITHIN each hash object, not a separate hash object on its own.
Using a simple array concat or + always makes the data a separate hash object. I've come across some good posts saying to use reduce along with merge, but those are Hash methods, not Array methods.
If I try to set data as a Hash instead of an array, I get
no implicit conversion of Hash into Array when I try to do
customer_phone.reduce({}, :merge)
after running customer_phone += data
What is the proper way to append data to an existing Hash object?
maybe combine each and merge
base = [
{
"AccountCode": "111123456",
"AccountID": 123456,
"BalanceCurrent": "-8.0",
"Phone": "123456888",
}
]
data = {:match_found=>true, :transfer_flag=>false, :confirm_id=>2}
base.each { |el| el.merge!(data) }
#=> [{:AccountCode=>"111123456", :AccountID=>123456, :BalanceCurrent=>"-8.0", :Phone=>"123456888", :match_found=>true, :transfer_flag=>false, :confirm_id=>2}]
You can add attr_accessor to your Customer model like this
class Customer
attr_accessor :data
end
With your data array:
data_array = [
:match_found => true,
:transfer_flag => false,
:confirm_id => 2
]
Then, you can execute the query combined with each function:
customer_phone = Customer.lookup_phone(params[:Phone]).each {|e| e.data = data_array}
Access it:
customer_phone.first.data
To render json:
render json: customer_phone, methods: [:data]

iterate through array, adjust a virtual attribute and return array with adjusted value

I have a rails class called a tag:
class Tag < ActiveRecord::Base
attr_accessor :is_enabled_on_item # a virtual attribute for ouptutting json
and I want to adjust the is_enabled_on_item like the following:
t.each { |tmp| tmp.is_enabled_on_item=true if current_tag_ids.include?(tmp.tagid) }
to get like:
[
{
tagid:1,
phrase: "blue",
is_enabled_on_item: true
},
{
tagid:2,
phrase: "yellow",
is_enabled_on_item: false
}
]
I tried collect
t.collect { |tmp| tmp.is_enabled_on_item=true if current_tag_ids.include?(tmp.tagid) }
but itreturns [true, false]. How would I achieve what I want?
edit #1
I'd want it to return an array of tags.
Something like this would work but the intermediary arr seems unnecessary:
arr=[]
t.each do |tmp|
tmp.is_enabled_on_item=current_tag_ids.include?(tmp.tagid)
arr << tmp
end
arr
t.map do |tmp|
tmp.attributes.merge(is_enabled_on_item: current_tag_ids.include?(tmp.tagid))
end
Your take on collect is almost right, try this:
t.collect { |tmp| tmp.is_enabled_on_item=current_tag_ids.include?(tmp.tagid); tmp }
Note that assignment returns assigned value, while you have to return tags in block in order to collect tags.
It would be nice to move one step forward and use more canonical method and argument names:
tags.map { |tag| tag.is_enabled_on_item=current_tag_ids.include?(tag.tagid); tag }

Resources