How to collect many attributes from hash in ruby - ruby-on-rails

I'm wondering if it's possible to collet many attributes from a hash.
Currently using ruby 2.6.3
Something like that
hash = { name: "Foo", email: "Bar", useless: nil }
other_hash = hash[:name, :email]
The output should be another hash but without the useless key/value

You can use Ruby's built in Hash#slice:
hash = { name: "Foo", email: "Bar", useless: nil }
p hash.slice(:name, :email)
# {:name=>"Foo", :email=>"Bar"}
If using Rails you can use Hash#except which receives just the keys you want to omit:
p hash.except(:useless)
# {:name=>"Foo", :email=>"Bar"}

If useless keys are so for having nil values, you can also use Hash#compact:
h = { name: "Foo", email: "Bar", useless: nil }
h.compact #=> {:name=>"Foo", :email=>"Bar"}

Related

Rails 6 - Strong Parameters - allowing array

I am sending this simple hash as JSON to my controller:
{
"cars": [
{ "rego": "ABC123" }
]
}
In the controller, I am trying to allow the array of cars for further processing.
I tried the following:
params.permit(:cars)
params.permit(cars: [])
params.permit(:cars, cars: [])
In every attempt I am not getting anything in my filtered params:
DEBUG -- : Unpermitted parameters: :cars, :car, :user_username, :user_token
=> <ActionController::Parameters {} permitted: true>
I am using RoR 6.0.2.1 with Ruby 2.6.5.
Try params.permit(cars: [:rego])
params.permit(cars: []) allows cars as an array of primitive values
{
"cars": [1, 2, 3, 4]
}
"Strong Parameters" has more information.

Get emails as array from an array of hashes by matching criteria

I have an array of hashes like this:
arr = [
{ email: 'prathab#hotmail.in', valid: true },
{ email: 'another#gmail.com', valid: false },
{ email: 'hello#hotmail.in', has_many: 10, valid: true}
]
What I need is to get list of emails by valid: true.
Expected result:
=> ["prathab#hotmail.in", "hello#hotmail.in"]
# another#gmail.com is not in the list because it's valid is false.
How can I check for such hash in the array without using each loop?
Currently I am doing this:
found = []
arr.each do|v|
if v[:valid] == true
found << v[:email]
end
end
Note: email and valid can be re-ordered or can have other keys along with them. I just minimized the example.
Try with select:
valids = arr.select { |hash| hash[:valid] }
emails = valids.map { |hash| hash[:email] }
Just for having an alternative way:
arr.collect { |h| h[:email] if h[:valid] }.compact
#=> ["prathab#hotmail.in", "hello#hotmail.in"]

Ruby - Access value from json array

I am creating an array of fields
def create_fields fields
fields_list = []
fields.each do |field|
# puts "adding_field to array: #{field}"
field_def = { field: field, data: { type: 'Text', description: '' } }
fields_list.push field_def
end
fields_list
end
The fields_list is being set to a jsonb field.
Lets say I pass in
create_fields ['Ford', 'BMW', 'Fiat']
Json result is an array:
{"field"=>"Ford", "data"=>{"type"=>"Text", "description"=>""}}
{"field"=>"BMW", "data"=>{"type"=>"Text", "description"=>""}}
{"field"=>"Fiat", "data"=>{"type"=>"Text", "description"=>""}}
How can I access the 'Ford' from the json array? Am i creating the array incorrectly? Is there a better way to create this array so I can access the field i want?
This assertion passes assert_equal(3, fields.count)
However i want to get 'Ford' and check it's properties, e.g. type = 'Text', type could equal 'Number' or whatever.
The result of your create_fields method with the specified parameters is the following:
[
{:field=>"Ford", :data=>{:type=>"Text", :description=>""}},
{:field=>"BMW", :data=>{:type=>"Text", :description=>""}},
{:field=>"Fiat", :data=>{:type=>"Text", :description=>""}}
]
It means that if you want to access the line belonging to "Ford", you need to search for it like:
2.3.1 :019 > arr.select{|e| e[:field] == "Ford" }
=> [{:field=>"Ford", :data=>{:type=>"Text", :description=>""}}]
2.3.1 :020 > arr.select{|e| e[:field] == "Ford" }[0][:data][:type]
=> "Text"
This is not optimal, because you need to search an array O(n) instead of using the pros of a hash. If there are e.g.: 2 "Ford" lines, you'll get an array which contains 2 elements, harder to handle collisions in field value.
It would be better if you created the array like:
def create_fields fields
fields_list = []
fields.each do |field|
# puts "adding_field to array: #{field}"
field_def = [field, { type: 'Text', description: '' } ]
fields_list.push field_def
end
Hash[fields_list]
end
If you choose this version, you can access the members like:
2.3.1 :072 > arr = create_fields ['Ford', 'BMW', 'Fiat']
=> {"Ford"=>{:type=>"Text", :description=>""}, "BMW"=>{:type=>"Text", :description=>""}, "Fiat"=>{:type=>"Text", :description=>""}}
2.3.1 :073 > arr["Ford"]
=> {:type=>"Text", :description=>""}
2.3.1 :074 > arr["Ford"][:type]
=> "Text"
Both of the above examples are Ruby dictionaries / Hashes.
If you want to create a JSON from this, you will need to convert it:
2.3.1 :077 > require 'json'
=> true
2.3.1 :078 > arr.to_json
=> "{\"Ford\":{\"type\":\"Text\",\"description\":\"\"},\"BMW\":{\"type\":\"Text\",\"description\":\"\"},\"Fiat\":{\"type\":\"Text\",\"description\":\"\"}}"
This is a structure that makes more sense to me for accessing values based on known keys:
def create_fields fields
fields_hash = {}
fields.each do |field|
fields_hash[field] = {type: 'Text', description: ''}
end
fields_hash
end
# The hash for fields_hash will look something like this:
{
Ford: {
type: "Text",
description: ""
},
BMW: {...},
Fiat: {...}
}
This will allow you to access the values like so: fields[:Ford][:type] in ruby and fields.Ford.type in JSON. Sounds like it would be easier to return an Object rather than an Array. You can access the values based on the keys more easily this way, and still have the option of looping through the object if you want.
Obviously, there are several ways of creating or accessing your data, but I'd always lean towards the developer picking a data structure best suited for your application.
In your case currently, in order to access the Ford hash, you could use the Ruby Array#detect method as such:
ford = fields_list.detect{|field_hash| field_hash['field'] == 'Ford' }
ford['data'] # => {"type"=>"Text", "description"=>""}
ford['data']['type'] # => 'Text'
So, you have result of your method:
result =
[
{"field"=>"Ford", "data"=>{"type"=>"Text", "description"=>""}},
{"field"=>"BMW", "data"=>{"type"=>"Text", "description"=>""}},
{"field"=>"Fiat", "data"=>{"type"=>"Text", "description"=>""}}
]
to get 'Ford' from it you can use simple method detect
result.detect { |obj| obj['field'] == 'Ford' }
#=> { "field"=>"Ford", "data"=>{"type"=>"Text", "description"=>""}
Also I recommend you to edit your method to make it more readable:
def create_fields(fields)
fields.map do |field|
{
field: field,
data: {
type: 'Text',
description: ''
}
}
end
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.

combine two arrays of possibly different sizes into hash

How can I combine these two arrays in to a hash. They may be of equal sizes or not.
#status_array = ["ready", "required", "processing", "approval", "live"]
#part_milestones = [#<Milestone id: 657707, data_type: "ready">, #<Milestone id: 657708, data_type: "required">, #<Milestone id: 657709, data_type: "approval">]
They are sorted already. I just need the hash to deal with the "blanks" properly like so:
{"ready"=>#<Milestone id: 657707, data_type: "ready">, "required"=>#<Milestone id: 657708, data_type: "required">, "processing"=>nil, "approval"=>#<Milestone id: 657709, data_type: "approval">, "live"=>nil}
The cleanest way I know to do this is:
hash = #status_array.inject({}) do |result_hash, status|
result_hash[status] = #part_milestones.select { |milestone| milestone.data_type == status }.first
result_hash
end
You can use zip to merge the arrays into two dimensional and then use the following to convert into hash
Hash[#status_array.zip(#part_milestones)]
Documentation for Hash[]
UPDATE:
just realized that its not a one to one mapping
hash = {}
#status_array.each do |status|
hash[status] = #part_milestone.find{|milestone| milestone.data_type == status}
end
#part_milestones.sort_by &:data_type should do the trick

Resources