Remove attribute from object recursivly - ruby-on-rails

I got a JSON string, which I would like to strip from some values. The problem is that the JSON object can contain child objects, which if they exist, I want to strip of the same sort of values (based on the key).
For example, I got this:
{
Title: "test",
Created: "2013-01-01",
ID: 1
Child: {
Title: "Test 2",
Created: "2013-01-02",
ID: 2,
RandomName: {
Title: "Test 3",
Created: "2013-01-05",
ID:3
}
}
}
I would like to remove the key "Created" from the objects and from all the child objects. Is there an easy way to achieve this in Ruby?

You can write a helper that calls the nested element recursively (example assuming you parsed the JSON to a hash)
def remove_recursive(hash)
hash.each do |key, value|
hash.delete(key) if key == "Created"
remove_recursive(hash[key]) if hash[key].kind_of?(Hash)
end
end

You can do it with a proc if you don't want to have a method just for this.
require 'json'
json_string = '{
"Title": "test",
"Created": "2013-01-01",
"ID": 1,
"Child": {
"Title": "Test 2",
"Created": "2013-01-02",
"ID": 2,
"RandomName": {
"Title": "Test 3",
"Created": "2013-01-05",
"ID": 3
}
}
}'
without_fields = proc do |h, *fields|
h = h.reject {|k,_| fields.include?(k) }
h.each do |k, v|
if v.is_a?(Hash)
h[k] = without_fields.call(v, *fields)
end
end
h
end
json_obj = JSON.load(json_string)
cleaned_obj = without_fields.call(json_obj, 'Created')
JSON.dump(cleaned_obj)
# => "{\"Title\":\"test\",\"ID\":1,\"Child\":{\"Title\":\"Test 2\",\"ID\":2,\"RandomName\":{\"Title\":\"Test 3\",\"ID\":3}}}"

Based on Tobias' answer, I modified the code to support child arrays etc. too. Mind you, I am fairly new to Ruby, so I am not sure this is a 100% complete.
The keysToRemove parameter is an array which contains the names of the keys I want to remove.
def remove_recursive(hash, keysToRemove)
if hash.kind_of?(Array)
hash.each do |h|
remove_recursive(h, keysToRemove)
end
elsif hash.kind_of?(Hash)
hash.each do |key, value|
if keysToRemove.include?(key)
hash.delete(key)
else
remove_recursive(hash[key], keysToRemove)
end
end
end
end

Related

How do I check if params is in a json file and if not throw error response?

I have a json file as below names.json. When you append the URL /list?name=Canada or /list?name=CANADA be it Uppercase or Lowercase, I want to check if the param[:name] is inside names.json file and throw error if not there.
[
{
"id": 1,
"name": "Canada"
},
{
"id": 17,
"name": "Denmark"
},
{
"id": 23,
"name": "Austria"
}
]
Here is what I have done but did not work…..
controller/concerns
require 'json'
JSON_NAMES = 'names.json'.freeze
module NameFileLoader
class JsonLoader
def self.json_data_hash
file = File.read(JSON_NAMES)
JSON.parse(file)
end
end
end
name_controller.rb
def check_name_validity_in_file
data = NameFileLoader::JsonLoader.json_data_hash
name = data.each { |item| item['name'] } # The problem is here.
if name.include?(params[:name])
{ errorCode: 400, message: 'Name provided is not valid' }
end
end
You’d better cache the JSON once loaded from the file in the first place. Also you probably want to maintain a cached list of allowed countries in the lowercase to compare.
module NameFileLoader
class JsonLoader
class << self
def json_data_hash
#json ||= JSON.parse(File.read(JSON_NAMES))
end
def countries
#countries ||= json_data_hash.map { |h| h['name'].downcase }
end
end
end
end
Now upon receival a parameter you might check it as:
if NameFileLoader::JsonLoader.countries.include?(params[:name].downcase)
...
end

how to create an array of hashes by looping over array of objects

I have following array of hash. I am trying to loop over it and build an array of hash of values of id and product_order_id.
objects =
[
#<Product: 0x00007ffd4a561108
#id="1",
#product_id="2",
#product_order_id="23",
#description="abc",
#status="abcde",
#start_date=nil,
#end_date=nil>,
#<Product: 0x00007ffd4a560c80
#id="45",
#product_id="22",
#product_order_id="87",
#description="ahef",
#status="gesff",
#start_date=nil,
#end_date=nil>
......more objects.....
]
This is what it should look like
[{ "1": "23" }, { "45": "87" }] -->its going to be uuid
I tried doing this but no luck
def mapped_product(objects)
mapping = []
objects.each do |object|
mapping << {
object.product_order_id: object.id
}
end
end
Any idea?
inline solution:
> Hash[objects.map{|p| [p.id, p.product_order_id] }]
# Output : [{ 1=>23 }, { 45=>87 }]
I'd usually implement it using an each_with_object
objects.each_with_object({}) { |obj, acc| acc[obj.id] = obj.product_order_id }
Unless I reaaaly want to squeeze some performance, than I'd go with Gagan's answer
Have you tried this?
def mapped_product(objects)
mapping = []
objects.each do |object|
mapping << {
object.id => object.product_order_id # I'm using an `=>` here
}
end
mapping # return the new mapping
end
I've just changed the : on the hash for a => to "make it dynamic" and swapped the values of id and product_order_id
You can also use a map here:
def mapped_product(objects)
objects.map do |object|
{ object.id => object.product_order_id }
end
end

Rails iterate multiple hash

I have the following hash :
{
"2017-01-01" => {
"2"=> [
{:a=>"2017-01-01", :b=>"2", :c=>"1"},
{:a=>"2017-01-01", :b=>"2", :c=>"2"}
]
},
"2017-01-02" => {
"5"=> [
{:a=>"2017-01-02", :b=>"5", :c=>"1"}
]
}
}
I would iterate separately
1)first iteration
{
{:a=>"2017-01-01", :b=>"2", :c=>"1"},
{:a=>"2017-01-01", :b=>"2", :c=>"2"}
}
2) second iteration
{
{:a=>"2017-01-02", :b=>"5", :c=>"1"}
}
How can I do? Thanks in advance.
answer for your question is in How to iterate over a hash in Ruby?
check it.
hash.each do |key, array|
puts array
end
if 'array' again is a hash, then you need to loop it as follows
hash.each do |key, hash2|
hash2.each do |key2,array|
puts array
end
end

RUBY - Correct way of doing array of hashes inside of hash

i need to do an array of hashes inside of a hash, something like this:
merit_hash => {
students => [
{
"id": id,
"name": name,
subjects => [
{
"id": id,
"grade": grade
},
{
"id": id,
"grade": grade
}
]
},
{
"id": id,
"name": name,
subjects => [
{
"id": id,
"grade": grade
},
{
"id": id,
"grade": grade
}
]
}
]
}
Right now, i just have the array of student hashes, but i dont exactly know how to put the subject array inside of it, im doing this:
merit = {}
merit["students"] = []
students.each do |students|
student_subjects = Array.new
merit["students"].push(
{
"id" => students.id,
"name" => students.name.to_s
selected_batch_subjects.each do |subjects|
grade = FinalGrades.where(batch_subject_id:subjects.id, period_id: period.id, student_id: student.id).first.value
student_subjects.push(
{
"id" => subjects.id,
"grade"=> grade
}
)
end
}
)
end
but throws this error
unexpected '}', expecting keyword_end
when i try to close the student hash... what can i do to make this work? or, whats the best way of implementing this?
Thanks!
Something like this should work:
merit = {}
merit["students"] = []
students.each do |student|
student_information = {"id" => student.id, "name" => student.name.to_s}
student_subjects = []
selected_batch_subjects.each do |subjects|
grade = FinalGrades.where(batch_subject_id:subjects.id, period_id: period.id, student_id: student.id).first.value
student_subjects.push({"id" => subjects.id, "grade" => grade})
end
student_information[:subjects] = student_subjects
merit["students"].push(student_information)
end
The important part is adding each student's subjects to the already existing hash.
Your iterations are not very clear to me but for current loop and array push you could do like this:
merit = {}
merit["students"] = []
students.each do |students|
student_subjects = []
merit["students"] << {
"id" => students.id,
"name" => students.name.to_s
}
selected_batch_subjects.each do |subjects|
grade = FinalGrades.where(batch_subject_id:subjects.id, period_id: period.id, student_id: student.id).first.value
student_subjects << {"id" => subjects.id,"grade"=> grade}
end
end

JBuilder loop that produces hash

I need loop that produces hash, not an array of objects. I have this:
json.service_issues #service.issues do |issue|
json.set! issue.id, issue.name
end
that results:
service_issues: [
{
3: "Not delivered"
},
{
6: "Broken item"
},
{
1: "Bad color"
},
{
41: "Delivery problem"
}
]
I need this:
service_issues: {
3: "Not delivered",
6: "Broken item",
1: "Bad color",
41: "Delivery problem"
}
Is it possible to do this without converting AR result to hash manually?
Jbuilder dev here.
Short answer: Yes. It's possible without converting array of models into hash.
json.service_issues do
#service.issues.each{ |issue| json.set! issue.id, issue.name }
end
but it'd probably be easier to prepare hash before-hand.
json.service_issues Hash[#service.issues.map{ |issue| [ issue.id, issue.name ] }]
For anyone who is interested in having an hash of arrays (objects), you can use the following code:
#bacon_types.each do |bacon_type|
json.set! bacon_type.name, bacon_type.bacons do |bacon|
bacon.title bacon.title
...
end
You can do it like this way
Jbuilder.encode do |json|
json.service_issues #service.issues.inject({}) { |hash, issue| hash[issue.id] = issue.name; hash }
end
The code generating hash technique may be understood by following example.
[1] pry(main)> array = [{id: 1, content: 'a'}, {id: 2, content: 'b'}]
=> [{:id=>1, :content=>"a"}, {:id=>2, :content=>"b"}]
[2] pry(main)> array.inject({}) { |hash, element| hash[element[:id]] = element[:content]; hash }
=> {1=>"a", 2=>"b"}
The key point of inject to generate hash, return created hash every after inserting new element. Above example, it is realized by ; hash.

Resources