Parsing table in Ruby - ruby

For learning purpose, i wanted to parse db table and save in a hash based on type for example table looks like:
source table in json format look like:
[
{"id": 1 , "type": "Atest", "ip":"10.2.0.1", "port":1234, "description": "xyz"},
{"id": 2 , "type": "Btest", "ip":"10.2.0.2", "port":3000, "description": "xyz"},
{"id": 3 , "type": "subtest", "ip":"10.2.0.3", "port":3001, "description": "xyz"},
{"id": 4 , "type": "Atest", "ip":"10.2.0.4", "port":9999, "description": "xyz"},
{"id": 5 , "type": "subtest", "ip":"10.2.0.5", "port":1235, "description": "xyz"},
{"id": 6 , "type": "Ctest", "ip":"10.2.0.6", "port":1111, "description": "xyz"},
{"id": 7 , "type": "Btest", "ip":"10.2.0.7", "port":2222, "description": "xyz"},
{"id": 8 , "type": "subtest", "ip":"10.2.0.8", "port":3333, "description": "xyz"}
]
what i like to do is to parse this table with only type (Atest and Btest) and save to a hash like
hash_result ={type: Atest {id: 1 ,description: xyz}, type: Btest {id:2 ,description: xyz}, type: Atest {id: 4 ,description: xyz} , type: Btest {id: 7 ,description: xyz}}
what i am thinking is to have a switch case on type like
hash_result={}
when type == "Atest"
hash_result.append(...)
when type == "Btest"
hash_result.append(...)
Result:
hash_result ={type: Atest {id: 1 ,description: xyz}, type: Btest {id:2 ,description: xyz}, type: Atest {id: 4 ,description: xyz} , type: Btest {id: 7 ,description: xyz}}
But i am not sure how to do this, in the end I also like to know after i have a hash_result, how to search for specific [id] and print its type. I will appreciate any guidance

Since your result hash is not a valid Hash I only assume the wanted result and give you general idea how to achieve it. Also I'm not sure if you prefer strings or symbols as the Hash keys. Since your data example uses symbols, I will stick with it.
For filtering use Hash#select to find an element based on ID use Enumerable#find
data = [
{"id": 1 , "type": "Atest", "ip":"10.2.0.1", "port":1234, "description": "xyz"},
{"id": 2 , "type": "Btest", "ip":"10.2.0.2", "port":3000, "description": "xyz"},
{"id": 3 , "type": "subtest", "ip":"10.2.0.3", "port":3001, "description": "xyz"},
{"id": 4 , "type": "Atest", "ip":"10.2.0.4", "port":9999, "description": "xyz"},
{"id": 5 , "type": "subtest", "ip":"10.2.0.5", "port":1235, "description": "xyz"},
{"id": 6 , "type": "Ctest", "ip":"10.2.0.6", "port":1111, "description": "xyz"},
{"id": 7 , "type": "Btest", "ip":"10.2.0.7", "port":2222, "description": "xyz"},
{"id": 8 , "type": "subtest", "ip":"10.2.0.8", "port":3333, "description": "xyz"}
]
filtered = data.select{ |d| %w(Atest Btest).include? d[:type] };
# => [
# {:id=>1, :type=>"Atest", :ip=>"10.2.0.1", :port=>1234, :description=>"xyz"},
# {:id=>2, :type=>"Btest", :ip=>"10.2.0.2", :port=>3000, :description=>"xyz"},
# {:id=>4, :type=>"Atest", :ip=>"10.2.0.4", :port=>9999, :description=>"xyz"},
# {:id=>7, :type=>"Btest", :ip=>"10.2.0.7", :port=>2222, :description=>"xyz"}
# ]
result_hash = filtered.map{ |line| {line[:type].to_sym => line.select{ |key, value| %i(id description).include? key }} }
# => [
# {:Atest=>{:id=>1, :description=>"xyz"}},
# {:Btest=>{:id=>2, :description=>"xyz"}},
# {:Atest=>{:id=>4, :description=>"xyz"}},
# {:Btest=>{:id=>7, :description=>"xyz"}}
# ]
result_hash.find{|element| element.values.first[:id] == 2}
# => {:Btest=>{:id=>2, :description=>"xyz"}}

Related

Add new items to multidimensional Ruby hash in loop

Migrating my code to Ruby stuck on the updating this hash with new items inside the multidimensional hash>customers>image>
bannerhash = {
"stooge": "larry",
"toppings": [],
"customers": [
{
"id": 1,
"alt": "Image seo text",
"image": {
"label": "gid:///28663588552955",
"image": "https://cdn.doma.com/s/files/1/0611/2064/3323/files/yellow-pillow-bedside-table_2000x.jpg?v=1637223489"
},
"mainsize": 100
},
{
"id": 2,
"alt": "Image seo text",
"image": {
"label": "gid:///28663588487419",
"image": "https://cdn.doma.com/s/files/1/0611/2064/3323/files/bed-side-table_2000x.jpg?v=1637223489"
},
"mainsize": 100
},
{
"id": 3,
"alt": "image",
"themeposition": "388506648827/posA",
"image": {
"label": "gid:///28663588585723",
"image": "https://cdn.doma.com/s/files/1/0611/2064/3323/files/sunlight-creeps-through-a-bright-living-room_2000x.jpg?v=1637223489"
},
"mainsize": 87
},
{
"id": 4,
"alt": "short width",
"themeposition": "home",
"mainsize": 70,
"image": {
"label": "gid:///28663588454651",
"image": "https://cdn.doma.com/s/files/1/0611/2064/3323/files/modern-and-stylish-design_2000x.jpg?v=1637223489"
},
"quickcss": "#import url(\'https://fonts.googleapis.com/css2?family=Comic+Neue:wght#300&display=swap\');\n.wrapper-4 .--description {font-family: \'Comic Neue\', cursive; }"
}
]
}
Want to update in the loop and the add the "newitem" key with value inside the "image" like
{
"stooge": "larry",
"toppings": [],
"customers": [
{
"id": 1,
"alt": "Image seo text",
"image": {
"label": "gid:///28663588552955",
"image": "https://cdn.doma.com/s/files/1/0611/2064/3323/files/yellow-pillow-bedside-table_2000x.jpg?v=1637223489",
"newitem": "https://cdn.doma.com/s/files/1/0611/2064/3323/files/yellow-pillow-bedside-table_3300x.jpg?v=1637223489"
},
"mainsize": 100
},
{
"id": 2,
"alt": "Image seo text",
"image": {
"label": "gid:///28663588487419",
"image": "https://cdn.doma.com/s/files/1/0611/2064/3323/files/bed-side-table_2000x.jpg?v=1637223489",
"newitem": "https://cdn.doma.com/s/files/1/0611/2064/3323/files/bed-side-table_3300x.jpg?v=1637223489"
},
"mainsize": 100
}
]
}
My workaround code where I am stuck on how ruby update hash inside the each loop. First I catch the inner hash with the "customers" key and know the index and value however stuck on how to add "newitem" inside the "customers" "image" hash
if bannerhash.has_key?(:customers)
puts "found"
bannerhash.each_with_index do |(key, value), index|
if key == :customers
puts "index: #{index} | key: #{key} | value: #{value}"
# STACK Here bannerhash[key].each
end
end
else
puts "banners not found"
end
This seems pretty straightforward. No need to loop over all of the key/value pairs looking for :customers when we can access it directly.
if bannerhash.has_key?(:customers)
bannerhash[:customers].each { |h|
h[:image][:newitem] = "https://cdn.doma.com/s/files/1/0611/2064/3323/files/yellow-pillow-bedside- table_3300x.jpg?v=1637223489"
}
else
puts "banners not found"
end

Trying to get foraign key records where every connected records are deactive ( Laravel elequent query )

I have 2 tables
Category's ( id, name )
Sub_categories ( id, key, value, category_id )
I'm trying to get all Category's whose all sub_categories are deactivated ( means are soft-deleted )
let me explain more
i have sub_categories data like this
[
{
"id": 1,
"category_id": 1,
"key": "sub 1",
"value": "sub_1",
"deleted_at": null
},
{
"id": 2,
"category_id": 1,
"key": "sub 2",
"value": "1",
"deleted_at": null
},
{
"id": 4,
"category_id": 1,
"key": "sub 3",
"value": "1",
"deleted_at": "2019-07-09 06:06:01"
},
{
"id": 5,
"category_id": 2,
"key": "sub 1",
"value": "33",
"deleted_at": "2019-07-09 06:06:01"
},
{
"id": 6,
"category_id": 2,
"key": "sub 2",
"value": "33",
"deleted_at": "2019-07-09 06:06:01"
}
]
i want only category_id -> 2 ( where all sub_categories are softedeleted )
hear's category model code
public function subCategory() {
$this->makeVisible('deleted_at');
return $this->hasMany('App\SubCategory','category_id','id');
}
$categories = Categories::doesntHave('subCategory')->get();
you first have to define Relationship between Categorys and Sub_categories - https://laravel.com/docs/5.8/eloquent-relationships.
Then use query to get what you want https://laravel.com/docs/5.8/queries
Get IDs of categories with sub categories:
$categoryIdsWithSubCategories = SubCategory::get()->pluck('category_id')->toArray();
Get categories without sub categories:
$categoriesWithoutSubCategories = Category::whereNotIn('id', $categoryIdsWithSubCategories)->get();

Transferring JSON Data into an array using ruby

This is my JSON code
{
"jobs": [
{
"id": 1,
"title": "Software Developer",
"applicants": [
{
"id": 1,
"name": "Rich Hickey",
"tags": ["clojure", "java", "immutability", "datomic", "transducers"]
},
{
"id": 2,
"name": "Guido van Rossum",
"tags": ["python", "google", "bdfl", "drop-box"]
}
]
},
{
"id": 2,
"title": "Software Architect",
"applicants": [
{
"id": 42,
"name": "Rob Pike",
"tags": ["plan-9", "TUPE", "go", "google", "sawzall"]
},
{
"id": 2,
"name": "Guido van Rossum",
"tags": ["python", "google", "bdfl", "drop-box"]
},
{
"id": 1337,
"name": "Jeffrey Dean",
"tags": ["spanner", "BigTable", "MapReduce", "deep learning", "massive clusters"]
}
]
}
]
}
I want to put the list of "Jobs" in an array using ruby.
I have the following code so far.
require 'json'
file = File.read(filepath)
data_hash = JSON.parse(file)
How do I iterate on the data_hash and chose what information I want and place it in an array?
You can use Array#each because data_hash['jobs'] contains an array of jobs:
data_hash['jobs'].each {|job| ... }
Like this,
arr = Array.new
data_hash.each { |job|
arr.insert(job['name'])
}
use Array#map for shorter code
data_hash['jobs'].map do |job|
# Do whatever you want with the job here
properties = %w(title applicants)
job.select{ |key| properties.include?(key) }
end

Text field in Rails stores string instead of Hash

I am designing a controller that handles the receipts for my E-commerce site. This is what the JSON params look like:
{
"receipt": {
"items": {
"item1": {
"price": "22.11",
"quantity": "2",
"name": "Name",
"discount": 0.04
},
"item2": {
"price": 12.11,
"quantity": 1,
"name": "Name"
},
"item3": {
"price": 21.11,
"quantity": 1,
"name": "Name",
"discount": 0.14
}
},
"payment_type": "Credit Card",
"payment_provider": "Visa",
"paid_status": true,
"total": 22
}
}
The items field is a text field in the database and I am using serialize to convert it to hash:
serialize :receiptitems, Hash
My strong params are defined in this way:
params.require(:receipt).permit(:payment_type, :payment_provider, :paid_status, :total, items: params[:receipt][:items].try(:keys))
Since the items are dynamically generated I now receive an error:
Unpermitted parameters: item1, item2, item3
How can I fix this? I tried tap method and it did not work as well.
You need to add the line
accepts_nested_attributes_for :price, :quality, :name, :discount
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

Merging records in JSON with Ruby

I have two json files that I'm trying to merge. The JSONs have different formatting (see below). I'd like to merge records, so [0] from file one and [0] from file two would become one record [0] in the new merged file.
The first JSON (file_a.json), appears like so:
{
"query": {
"count": 4,
"created": "2012-11-21T23:07:00Z",
"lang": "en-US",
"results": {
"quote": [
{
"Name": "Bill",
"Age": "46",
"Number": "3.55"
},
{
"Name": "Jane",
"Age": "33",
"Number": nil
},
{
"Name": "Jack",
"Age": "55",
"Number": nil
},
{
"Name": "Xavier",
"Age": nil,
"Number": "153353535"
}
]
}
}
}
The second JSON (file_b.json) appears like so:
[
{
"Number2": 25253,
"Number3": 435574,
"NAME": "Bill"
},
{
"Number2": 345353,
"Number3": 5566,
"NAME": "Jane"
},
{
"Number2": 56756,
"Number3": 232435,
"NAME": "Jack"
},
{
"Number2": 7457,
"Number3": 45425,
"NAME": "Xavier"
}
]
None of the keys are the same in both JSONs (well, actually "Name" is a key in both, but in the first the key is "Name" and in the second its "NAME" - just so I can check that the merge works correctly - so I want "Name" and "NAME" in the final JSON), the first record in the first file matches with the first record in the second file, and so on.
So far, I tried merging like this:
merged = %w[a b].inject([]) { |m,f| m << JSON.parse(File.read("file_#{f}.json")) }.flatten
But this of course merged them, but not how I wanted them merged (they are merged sucessively, and because of the different formatting, it gets quite ugly).
I also tried merging like this:
a = JSON.parse(File.read("file_a.json"))
b = JSON.parse(File.read("file_b.json"))
merged = a.zip(b)
Came closer but still not correct and the formatting was still horrendous.
In the end, what I want is this (formatting of second JSON - headers from first JSON can be junked):
[
{
"Name": "Bill",
"Age": 46,
"Number": 3.55,
"Number2": 25253,
"Number3": 435574,
"NAME": "Bill"
},
{
"Name": "Jane",
"Age": 33,
"Number": nil,
"Number2": 345353,
"Number3": 5566,
"NAME": "Jane"
},
{
"Name": "Jack",
"Age": 55,
"Number": nil,
"Number2": 56756,
"Number3": 232435,
"NAME": "Jack"
},
{
"Name": "Xavier",
"Age": nil,
"Number": 153353535,
"Number2": 7457,
"Number3": 45425,
"NAME": "Xavier"
}
]
Any help is appreciated. Thanks a lot.
Hеllo, seems format changed from last time :)
UPDATE: more readable version that also convert corresponding values to integers/floats:
require 'json'
require 'ap'
a = JSON.parse(File.read('./a.json'))['query']['results']['quote'] rescue []
b = JSON.parse(File.read('./b.json'))
final = []
a.each_with_index do |ah,i|
unless bh = b[i]
bh = {}
puts "seems b has no #{i} key, merging skipped"
end
final << ah.merge(bh).inject({}) do |f, (k,v)|
if v.is_a?(String)
if v =~ /\A\d+\.\d+\Z/
v = v.to_f
elsif v =~ /\A\d+\Z/
v = v.to_i
end
end
f.update k => v
end
end
ap final
will display:
[
[0] {
"Name" => "Bill",
"Age" => 46,
"Number" => 3.55,
"Number2" => 25253,
"Number3" => 435574,
"NAME" => "Bill"
},
[1] {
"Name" => "Jane",
"Age" => 33,
"Number" => nil,
"Number2" => 345353,
"Number3" => 5566,
"NAME" => "Jane"
},
[2] {
"Name" => "Jack",
"Age" => 55,
"Number" => nil,
"Number2" => 56756,
"Number3" => 232435,
"NAME" => "Jack"
},
[3] {
"Name" => "Xavier",
"Age" => nil,
"Number" => 153353535,
"Number2" => 7457,
"Number3" => 45425,
"NAME" => "Xavier"
}
]
Here is a working demo
Btw, your json is a bit wrong in both files.
See the fixed versions here and here

Resources