I'm trying to flat a nested hash. My hash has this structure:
{
"errorDescription":"",
"message":"",
"resultCode":"OK",
"resultObj":{
"categoryList":[
{
"asNew":"N",
"categoryId":40000000,
"categoryList":[
{
"asNew":"N",
"categoryId":40000007,
"categoryList":[
],
"categoryName":"CATALOGO",
"categoryType":"TYPE_NODE",
"contentId":40000007,
"contentTitle":"CATALOGO",
"contentType":"CATEGORY_NODE",
"orderId":0,
"urlPictures":""
},
{
"asNew":"N",
"categoryId":40000018,
"categoryList":[
{
"asNew":"N",
"categoryId":40000019,
"categoryList":[
],
"categoryName":"CANALI CALCIO",
"categoryType":"TYPE_VOD",
"contentId":40000019,
"contentTitle":"CALCIO & SPORT",
"contentType":"CATEGORY_LEAF",
"orderId":0,
"urlPictures":""
},
{
"asNew":"N",
"categoryId":40000020,
"categoryList":[
],
"categoryName":"CANALI CINEMA",
"categoryType":"TYPE_VOD",
"contentId":40000020,
"contentTitle":"CINEMA & SERIE",
"contentType":"CATEGORY_LEAF",
"orderId":1,
"urlPictures":""
}
],
"categoryName":"CANALI TV",
"categoryType":"TYPE_NODE",
"contentId":40000018,
"contentTitle":"CANALI TV",
"contentType":"CATEGORY_NODE",
"orderId":1,
"urlPictures":""
}
],
"categoryName":"ROOT",
"categoryType":"TYPE_NODE",
"contentId":40000000,
"contentTitle":"ROOT",
"contentType":"",
"orderId":0,
"urlPictures":""
}
]
}
}
I must convert this hash in an array of objects, where every object has this structure:
{
"asNew":"N",
"categoryId":40000007,
"categoryName":"CATALOGO",
"categoryType":"TYPE_NODE",
"contentId":40000007,
"contentTitle":"CATALOGO",
"contentType":"CATEGORY_NODE",
"orderId":0,
"urlPictures":""
}
Basically every key "categoryList" has an array as value and this array has one or more hash with another key "categoryList". I must move all the objects in every categoryList array to another array without the categoryList 'key'.
edit
I add this method to Hash class
class Hash
def find_all_values_for(key)
result = []
result << self[key]
unless self.values.nil?
self.values.each do |values|
values = [values] unless values.is_a? Array
values.each do |value|
result += value.find_all_values_for(key) if value.is_a? Hash
end
end
end
result.flatten.compact
end
end
And I map the resulting array deleting the CategoryList key
h.map { |c| c.except!('categoryList') }
It works but meybe there is a more efficient way.
This code will return the requested Array of objects.
def flatten_categoryList(categoryList)
categoryList.inject([]) do |result, item|
result + flatten_categoryList(item.delete("categoryList")) << item
end
end
flatten_categoryList(h["resultObj"]["categoryList"])
I was unable to run yours to get a benchmark, so judging the solutions is currently subjective.
Related
I am new to Ruby, could someone help?
I have some product data Json that I need to sort by the expiry date, however everything thing I have tried with .sort_by so far is erroring.
The Json is in this format
{"wh_Repeating":[
{
"wh": {
"Item_Number": "111166",
"Expiry_Date": "2023-05-05"
}
},
{
"wh": {
"Item_Number": "111167",
"Expiry_Date": "2023-05-01"
}
},
{
"wh": {
"Item_Number": "111168",
"Expiry_Date": "2023-05-09"
}
}]}
in Ruby that shows as
{:wh_Repeating=>[
{:wh=>{:Item_Number=>"111166", :Expiry_Date=>"2023-05-05"}},
{:wh=>{:Item_Number=>"111167", :Expiry_Date=>"2023-05-01"}},
{:wh=>{:Item_Number=>"111168", :Expiry_Date=>"2023-05-09"}}
]}
tried alsorts
latest attempt was
sorted = jsonIn["wh_Repeating"]
sorted.sort_by { |k,v| v[:"Expiry_Date"] }
puts sorted
which gave me
undefined method `sort_by' for nil:NilClass (NoMethodError)
(Exception)
Your hash keys are symbols not strings.
jsonIn["wh_Repeating"] should be jsonIn[:wh_Repeating]
Also, sorted.sort_by { |k,v| v[:"Expiry_Date"] } does not mutate sorted.
sort_by does not mutate the receiver. In other words, the value of sorted remains the same. There is a bang version (sort_by!) that does mutate (a side-effect) but the use of mutating functions is discouraged.
This does what you want to do.
jsonIn[:wh_Repeating].sort_by { |h| h.dig(:wh, :Expiry_Date) }
I would do it like this:
data = {:wh_Repeating=>
[{:wh=>{:Item_Number=>"111166", :Expiry_Date=>"2023-05-05"}},
{:wh=>{:Item_Number=>"111167", :Expiry_Date=>"2023-05-01"}},
{:wh=>{:Item_Number=>"111168", :Expiry_Date=>"2023-05-09"}}]}
data[:wh_Repeating].sort_by! { |hash| hash[:wh][:Expiry_Date] }
data
#=> {:wh_Repeating=>
[{:wh=>{:Item_Number=>"111167", :Expiry_Date=>"2023-05-01"}},
{:wh=>{:Item_Number=>"111166", :Expiry_Date=>"2023-05-05"}},
{:wh=>{:Item_Number=>"111168", :Expiry_Date=>"2023-05-09"}}]}
I have assumed the original hash (h below) is not to be mutated (modified).
h = { :wh_Repeating=>[
{:wh=>{:Item_Number=>"111166", :Expiry_Date=>"2023-05-05"}},
{:wh=>{:Item_Number=>"111167", :Expiry_Date=>"2023-05-01"}},
{:wh=>{:Item_Number=>"111168", :Expiry_Date=>"2023-05-09"}}
]
}
As this hash has a single key (:wh_Repeating), we can simply write the structure of the desired hash as
{ :wh_Repeating=>... }
and then compute the value of :wh_Repeating.
{ :wh_Repeating=>h[:wh_Repeating].sort_by { |g| g[:wh][:Expiry_Date] } }
#=> { :wh_Repeating=>[
# {:wh=>{:Item_Number=>"111167", :Expiry_Date=>"2023-05-01"}},
# {:wh=>{:Item_Number=>"111166", :Expiry_Date=>"2023-05-05"}},
# {:wh=>{:Item_Number=>"111168", :Expiry_Date=>"2023-05-09"}}
# ]
# }
The original hash h is unchanged, which can be easily verified.
I'm trying to create JSON files dynamically, where the content of each file belongs only to a certain entity.
I have an array of Ruby objects, where each object represents an entity.
entities = [#<Entity:0x0055f364d9cd78 #name="entity-B", #internal_asn_number=64514, #classification_id="B">,#<Entity:0x0055f364d89070 #name="entity-A", #internal_asn_number=64513, #classification_id="A">]
And I have an array of hashes, which contains several rules for each entity and I would like to write these hashes to JSON files, one file per entity.
I have the following code:
array_hashes = [{"rulename"=>"entity-B_source_fqdn_1", "if"=>{"source.fqdn"=>"mail.tec.dsr.entityB.com"}, "then"=>{"event_description.text"=>"B"}}, {"rulename"=>"entity-B_destination_fqdn_1", "if"=>{"destination.fqdn"=>"mail.tec.dsr.entity_B.com"}, "then"=>{"event_description.text"=>"B"}}, {"rulename"=>"entity-A_source_fqdn_1", "if"=>{"source.fqdn"=>"194-65-57-128.entityA.com"}, "then"=>{"event_description.text"=>"A"}}, {"rulename"=>"entity-A_destination_fqdn_1", "if"=>{"destination.fqdn"=>"194-65-57-128.entityA.com"}, "then"=>{"event_description.text"=>"A"}}]
path = "/home/mf370/Desktop/RubyProject/"
file_name = "_url_fqdn.conf"
entities.each do |entity|
File.open(path + entity.name + file_name, "w") do |f|
array_hashes.each do |rule|
if rule['rulename'].match(entity.name)
f.write(JSON.pretty_generate([rule]))
end
end
end
This code works and creates the files dynamically, however the content of the files is not what I was expecting.
This is the output from the code above:
[
{
"rulename": "entity-A_source_fqdn_1",
"if": {
"source.fqdn": "194-65-57-128.entityA.com"
},
"then": {
"event_description.text": "A"
}
}
][
{
"rulename": "entity-A_destination_fqdn_1",
"if": {
"destination.fqdn": "194-65-57-128.entityA.com"
},
"then": {
"event_description.text": "A"
}
}
]
And this is the output that I was looking for:
[
{
"rulename": "entity-A_source_fqdn_1",
"if": {
"source.fqdn": "194-65-57-128.entityA.com"
},
"then": {
"event_description.text": "A"
}
},
{
"rulename": "entity-A_destination_fqdn_1",
"if": {
"destination.fqdn": "194-65-57-128.entityA.com"
},
"then": {
"event_description.text": "A"
}
}
]
Probably this is very simple to solve, but I ran out of ideas on how to solve this, I'm new to Ruby Programming. Thank you for your help!
The problem is that you pack each rule inside an array :
JSON.pretty_generate([rule])
You should create an array of matching rules, and dump it as JSON :
entities.each do |entity|
File.open(File.join(path, entity.name + file_name), "w") do |f|
matching_rules = array_hashes.select{ |rule| rule['rulename'].match(entity.name) }
f.write(JSON.pretty_generate(matching_rules))
end
end
The Problem is that you wrap each rule in an array.
THis should solve the issue:
json_entities = []
entities.each do |entity|
File.open(path + entity.name + file_name, "w") do |f|
array_hashes.each do |rule|
if rule['rulename'].match(entity.name)
json_entities << rule
end
end
end
end
f.write(JSON.pretty_generate(json_entities))
i have the following Array Object :
[
{
"key1": abc,
"key2":xyz
},
{
"key1": abc,
"key2":xyz
}
]
Now what i want is to print "key1" & "key2". I know we can iterate through values using map, but i also want to iterate through Array keys.
Assuming ArrayObj contains the key:value pairs, we can do the following:
let keys = Object.keys(ArrayObj);
for(index=0;index<keys.length;index++)
{
console.log(keys[index]);
}
I have an array of hashes that I would like to sort based on the :reference values. But some of the elements within the array do not have a :reference key and therefore cannot be sorted. Is there a way to ignore this field and just sort the hash elements that contain this key?
I've tried the following approach but I'm getting an argument error
ArgumentError: comparison of NilClass with String failed
sort_by at org/jruby/RubyEnumerable.java:503
arr1 = [{:reference=> "F123",
:name=> "test4"
},
{
:reference=> "ZA4",
:name=> "test3"
},
{
:reference=> "A43",
:name=> "test2"
},
{
:name=> "test1"
},
{
:name=> "homework1"
}]
arr1 = arr1.sort_by { |hash| hash[:reference] }
puts arr1
The correct output should look like this :
=> arr1= [
{:reference=>"A43", :name=>"test2"},
{:reference=>"F123", :name=>"test4"},
{:reference=>"ZA4", :name=>"test3"},
{:name=> "test1"},
{:name=> "homework1"}
]
You can only sort on values that can be compared, so if you've got values of different types it's best to convert them to the same type first. A simple work-around is to convert to string:
arr1.sort_by { |hash| hash[:reference].to_s }
You can also assign a default:
arr1.sort_by { |hash| hash[:reference] || '' }
Edit: If you want the nil values sorted last:
arr1.sort_by { |hash| [ hash[:reference] ? 0 : 1, hash[:reference].to_s ] }
If you don't mind temporary variables, you could split the array into two arrays, one containing hashes with :reference and the other those without:
with_ref, without_ref = arr1.partition { |h| h[:reference] }
Then sort the first one:
with_ref.sort_by! { |h| h[:reference] }
And finally concatenate both arrays:
arr1 = with_ref + without_ref
I want to write a query in mongoDB where I want to find which elements of my array are present in database array 'upvotes' my query is given below
userupvotesmodel.findOne({mobile_no: "1234567890", upvotes : { $in : ["aa", "bdnvh", "563fa4408d88ea6c13c7abba", "djfufhgj", "5625bd9dbe545d2412d4ae62", "fjjshddhjfn", "djfhudh", "jfhshnffj", "sjdhfkskajdk"] } }, function(err, docs) {
if (err) {
console.log('Error Finding query results');
console.log(err);
res.json({success: 0, message : err});
return next(err);
} else {
if (docs) {
console.log('2 mins', currentdate.getMinutes());
console.log('2 secs', currentdate.getSeconds());
console.log('docs', docs);
}
}
});
Now for query above if any single value of my array is present in database then it sends whole upvotes array but I am not able to find out which elements were present for e.g "aa", "bdnvh" etc in database how can I write a query to know which elements are present in the database.
I want to find this in one query by querying for every single element; I know I can do this but I want to do this in any possible single mongoDB query.
I think you don't need to worry about the query and you can just use java script instead. Just iterate through the docs.upvotes array and compare its values with the array you provided.
var my_array = ["aa", "bdnvh", "563fa4408d88ea6c13c7abba", "djfufhgj", "5625bd9dbe545d2412d4ae62", "fjjshddhjfn", "djfhudh", "jfhshnffj", "sjdhfkskajdk"];
// ... your code
// inside of if(docs) statement
var matched_elements = [];
docs.upvotes.forEach(function(element, index, array) {
for (var i = 0; i < my_array.length; i++) {
if (my_array[i] == element) {
matched_elements.push(element);
}
}
});
console.log(matched_elements);
You could use the aggregation framework. First $unwind your upvotes array. Then $match on elements that are in the array provided by you. Than $group it back on _id(or whatever you want) creating "matched_elements" array with $addToSet or $push. The returned documents will have only upvotes elements (in "matched_elements array) that are also in your array.
var my_array = ["aa", "bdnvh", "563fa4408d88ea6c13c7abba", "djfufhgj", "5625bd9dbe545d2412d4ae62", "fjjshddhjfn", "djfhudh", "jfhshnffj", "sjdhfkskajdk"];
db.collection.aggregate( [
{ "$unwind" : "$upvotes" },
{ "$match": { "upvotes": {"$in": my_array} } },
{ "$group" : { "_id":"$_id","matched_elements":{ "$addToSet": "$upvotes" }}}
] );