Replace all HASH keys with another key - ruby-on-rails

I'm trying to replace all the keys inside a hash with another key, like this:
{
name: 'Josh',
surname: 'Simon',
cars: [{
name: 'Diablo',
seats: [{ name: 'Josh', surname: 'Simon' }],
},
{
name: 'Raptor',
seats: [{ name: 'Josh', surname: 'Simon' }],
},
{
name: 'Testarossa',
seats: [{ name: 'Josh', surname: 'Simon' }],
}],
}
let's say I want to change the cars key to people, so I can achieve something like this
{
name: 'Josh',
surname: 'Simon',
cars: [{
name: 'Diablo',
people: [{ name: 'Josh', surname: 'Simon' }],
},
{
name: 'Raptor',
people: [{ name: 'Josh', surname: 'Simon' }],
},
{
name: 'Testarossa',
people: [{ name: 'Josh', surname: 'Simon' }],
}],
}
can this be done somehow?

You can delete old key and assign that value to new one
hash =
{
name: 'Josh',
surname: 'Simon',
cars: [{
name: 'Diablo',
seats: [{ name: 'Josh', surname: 'Simon' }],
},
{
name: 'Raptor',
seats: [{ name: 'Josh', surname: 'Simon' }],
},
{
name: 'Testarossa',
seats: [{ name: 'Josh', surname: 'Simon' }],
}],
}
hash[:cars].each { |h| h[:people] = h.delete(:seats) }
hash
# => {
# name: 'Josh',
# surname: 'Simon',
# cars: [{
# name: 'Diablo',
# people: [{ name: 'Josh', surname: 'Simon' }],
# },
# {
# name: 'Raptor',
# people: [{ name: 'Josh', surname: 'Simon' }],
# },
# {
# name: 'Testarossa',
# people: [{ name: 'Josh', surname: 'Simon' }],
# }],
# }

If the variable hash holds the hash given in the example, and that object is to be mutated, one can write the following.
hash[:cars].each { |h| h.transform_keys! { |k| k == :seats ? :people : k } }
#=> [
# {
# :name=>"Diablo",
# :people=>[{:name=>"Josh", :surname=>"Simon"}]
# },
# {
# :name=>"Raptor",
# :people=>[{:name=>"Josh", :surname=>"Simon"}]
# },
# {
# :name=>"Testarossa",
# :people=>[{:name=>"Josh", :surname=>"Simon"}]
# }
# ]
​so now
hash
#=> {
# :name=>"Josh",
# :surname=>"Simon",
# :cars=>[
# {
# :name=>"Diablo",
# :people=>[{:name=>"Josh", :surname=>"Simon"}]
# },
# {
# :name=>"Raptor",
# :people=>[{:name=>"Josh", :surname=>"Simon"}]
# },
# {
# :name=>"Testarossa",
# :people=>[{:name=>"Josh", :surname=>"Simon"}]
# }
# ]
# }
See Hash#transform_keys!.
If the hash is not to be mutated you could operate on a deep copy. One of doing that is to use the methods Marshal::load and Marshal::dump:
h = Marshal.load(Marshal.dump(hash))
#=> {
# :name=>"Josh",
# :surname=>"Simon",
# :cars=>[
# {
# :name=>"Diablo",
# :seats=>[{:name=>"Josh", :surname=>"Simon"}]
# },
# {
# :name=>"Raptor",
# :seats=>[{:name=>"Josh", :surname=>"Simon"}]
# },
# {
# :name=>"Testarossa",
# :seats=>[{:name=>"Josh", :surname=>"Simon"}]
# }
# ]
# }
Note that if we alter h by writing, for example,
h[:name] = "Lola"
h[:cars][1][:seats][0] = "cats"
we may verify that hash is unchanged.

You can do it like this:
hash =
{
name: 'Josh',
surname: 'Simon',
cars: [{
name: 'Diablo',
seats: [{ name: 'Josh', surname: 'Simon' }],
},
{
name: 'Raptor',
seats: [{ name: 'Josh', surname: 'Simon' }],
},
{
name: 'Testarossa',
seats: [{ name: 'Josh', surname: 'Simon' }],
}],
}
hash[:cars].each { |record| record.store(:people, record.delete(:seats)) }

Related

How Do You Use A Database DataSet to Build a HighCharts Org Chart

I'm trying to create an org chart from my database. These are the fields that are returned in the dataset:
Product_ID
Parent_Product_ID
Product_Name
Product_Description
Manager_Name
Parent_Name
Level
When I look at the HighChart Org Chart Example I see the following attributes:
keys: ['from', 'to'],
data: [
['Shareholders', 'Board'],
['Board', 'CEO'],
['CEO', 'CTO'],
['CEO', 'CPO'],
['CEO', 'CSO'],
['CEO', 'HR'],
['CTO', 'Product'],
['CTO', 'Web'],
['CSO', 'Sales'],
['HR', 'Market'],
['CSO', 'Market'],
['HR', 'Market'],
['CTO', 'Market']
],
nodes: [{
id: 'Shareholders'
}, {
id: 'Board'
}, {
id: 'CEO',
title: 'CEO',
name: 'Grethe Hjetland',
image: 'https://wp-assets.highcharts.com/www-highcharts-com/blog/wp-content/uploads/2020/03/17131126/Highsoft_03862_.jpg'
}, {
id: 'HR',
title: 'HR/CFO',
name: 'Anne Jorunn Fjærestad',
color: '#007ad0',
image: 'https://wp-assets.highcharts.com/www-highcharts-com/blog/wp-content/uploads/2020/03/17131210/Highsoft_04045_.jpg'
}, {
id: 'CTO',
title: 'CTO',
name: 'Christer Vasseng',
image: 'https://wp-assets.highcharts.com/www-highcharts-com/blog/wp-content/uploads/2020/03/17131120/Highsoft_04074_.jpg'
}, {
id: 'CPO',
title: 'CPO',
name: 'Torstein Hønsi',
image: 'https://wp-assets.highcharts.com/www-highcharts-com/blog/wp-content/uploads/2020/03/17131213/Highsoft_03998_.jpg'
}, {
id: 'CSO',
title: 'CSO',
name: 'Anita Nesse',
image: 'https://wp-assets.highcharts.com/www-highcharts-com/blog/wp-content/uploads/2020/03/17131156/Highsoft_03834_.jpg'
}, {
id: 'Product',
name: 'Product developers'
}, {
id: 'Web',
name: 'Web devs, sys admin'
}, {
id: 'Sales',
name: 'Sales team'
}, {
id: 'Market',
name: 'Marketing team',
column: 5
}],
The variable "this.data" contains the database query results.
The Product_ID and Parent_Product_ID are the keys that tie the rows together.
The data appears to be the unique Product_ID and Parent_Product_ID values.
The Node is the rest of the data.
How do I convert my dataset to populate these 3 attributes?

How to get Ruby type from PostgreSQL type using Active Record

If I do MyClass.columns the columns will have a type attribute with values like :integer, :string, :datetime, etc. How can I take that type and map it to the ruby type?
For example:
:integer => Integer
:string => String
:datetime => DateTime
I assume Rails has a canonical lookup list that I could use but I can't seem to find it. Ideally, I would find a way that works for every DB adapter that Rails supports.
You can find the "type matches" for PostgreSQL in the source code on the official repo.
NATIVE_DATABASE_TYPES = {
primary_key: "bigserial primary key",
string: { name: "character varying" },
text: { name: "text" },
integer: { name: "integer", limit: 4 },
float: { name: "float" },
decimal: { name: "decimal" },
datetime: { name: "timestamp" },
time: { name: "time" },
date: { name: "date" },
daterange: { name: "daterange" },
numrange: { name: "numrange" },
tsrange: { name: "tsrange" },
tstzrange: { name: "tstzrange" },
int4range: { name: "int4range" },
int8range: { name: "int8range" },
binary: { name: "bytea" },
boolean: { name: "boolean" },
xml: { name: "xml" },
tsvector: { name: "tsvector" },
hstore: { name: "hstore" },
inet: { name: "inet" },
cidr: { name: "cidr" },
macaddr: { name: "macaddr" },
uuid: { name: "uuid" },
json: { name: "json" },
jsonb: { name: "jsonb" },
ltree: { name: "ltree" },
citext: { name: "citext" },
point: { name: "point" },
line: { name: "line" },
lseg: { name: "lseg" },
box: { name: "box" },
path: { name: "path" },
polygon: { name: "polygon" },
circle: { name: "circle" },
bit: { name: "bit" },
bit_varying: { name: "bit varying" },
money: { name: "money" },
interval: { name: "interval" },
oid: { name: "oid" },
}
I would add that it is indeed good to know how things work under the hood, one of the goals of the ORM is for you to not having to care about that. You are supposed to use your ruby data types in your migrations.
If you want to have a cross-database solution, just avoid to use types that are not matched to the same on each DBMS (like :decimal)

Optimize formatting from aggregated return from Sequel

I have a query from sequel that is returning something like this:
posts = [
<Post: #attributes={ id: 1, title: 'Foo', text: 'Bar', user_id: 21, user: <User: #attributes={ id: 21, name: 'John'}>}>,
<Post: #attributes={ id: 2, title: 'Bar', text: 'Foo', user_id: 21, user: <User: #attributes={ id: 21, name: 'John'}>}>,
<Post: #attributes={ id: 3, title: 'FooBar', text: 'FooBar', user_id: 19, user: <User: #attributes={ id: 19, name: 'Jane'}>}>
]
An array of Post and User objects.
I want to return it like this to the user:
json = {
posts:[
{ id: 1, title: 'Foo', text: 'Bar', user_id: 21 },
{ id: 2, title: 'Bar', text: 'Foo', user_id: 21 },
{ id: 3, title: 'FooBar', text: 'FooBar', user_id: 19 }
],
users: [
{ id: 21, name: 'John'},
{ id: 19, name: 'Jane'}
]
}
What would be the most efficient way to extract this Hash from the original array?
This is the code I'm using for it right now:
def prepare_json(array)
posts = []
users = Hash[]
array.each do |item|
posts.push(item.post)
# user id is unique so I use it to avoid duplication on
# the users array
users[item.user.id.to_sym] = item.user
end
{ posts: posts, users: users.values }
end
users = posts.map{|h| h.delete(:user)}.uniq
json = {posts: posts, users: users}
Result:
{
:posts=>[{:id=>1, :title=>"Foo", :text=>"Bar", :user_id=>21}, {:id=>2, :title=>"Bar", :text=>"Foo", :user_id=>21}, {:id=>3, :title=>"FooBar", :text=>"FooBar", :user_id=>19}],
:users=>[{:id=>21, :name=>"John"}, {:id=>19, :name=>"Jane"}]
}

List of ActiveRecord DB datatypes as symbols/strings

This is a really short question: Is there a list of all datatypes I can use for columns in a mysql database with ActiveRecord?
I know the datatypes and that they are all listed in the docs, but I want to know if there is already a collection of the types I could use to populate an html <select>. I'd like to avoid creating an own list for this if it already exists, but I can't seem to find one.
You can use this command (run on an app using PG)
ActiveRecord::Base.connection.native_database_types.keys
# [:primary_key, :string, :text, :integer, :float, :decimal,
:datetime, :time, :date, :daterange, :numrange, :tsrange,
:tstzrange, :int4range, :int8range, :binary, :boolean, :xml,
:tsvector, :hstore, :inet, :cidr, :macaddr, :uuid, :json, :jsonb,
:ltree, :citext, :point, :line, :lseg, :box, :path, :polygon,
:circle, :bit, :bit_varying, :money]
Postgres:
You can also browse the source code and get this massive array.
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L73-L114
NATIVE_DATABASE_TYPES = {
primary_key: "bigserial primary key",
string: { name: "character varying" },
text: { name: "text" },
integer: { name: "integer" },
float: { name: "float" },
decimal: { name: "decimal" },
datetime: { name: "timestamp" },
time: { name: "time" },
date: { name: "date" },
daterange: { name: "daterange" },
numrange: { name: "numrange" },
tsrange: { name: "tsrange" },
tstzrange: { name: "tstzrange" },
int4range: { name: "int4range" },
int8range: { name: "int8range" },
binary: { name: "bytea" },
boolean: { name: "boolean" },
xml: { name: "xml" },
tsvector: { name: "tsvector" },
hstore: { name: "hstore" },
inet: { name: "inet" },
cidr: { name: "cidr" },
macaddr: { name: "macaddr" },
uuid: { name: "uuid" },
json: { name: "json" },
jsonb: { name: "jsonb" },
ltree: { name: "ltree" },
citext: { name: "citext" },
point: { name: "point" },
line: { name: "line" },
lseg: { name: "lseg" },
box: { name: "box" },
path: { name: "path" },
polygon: { name: "polygon" },
circle: { name: "circle" },
bit: { name: "bit" },
bit_varying: { name: "bit varying" },
money: { name: "money" },
interval: { name: "interval" },
oid: { name: "oid" },
}
Mysql
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb#L41-L55
NATIVE_DATABASE_TYPES = {
primary_key: "bigint auto_increment PRIMARY KEY",
string: { name: "varchar", limit: 255 },
text: { name: "text", limit: 65535 },
integer: { name: "int", limit: 4 },
float: { name: "float" },
decimal: { name: "decimal" },
datetime: { name: "datetime" },
timestamp: { name: "timestamp" },
time: { name: "time" },
date: { name: "date" },
binary: { name: "blob", limit: 65535 },
boolean: { name: "tinyint", limit: 1 },
json: { name: "json" },
}

Group orderring

I am looking to group a list of projects by a nested field, in this case custom_field.value when a certain id is passed in.
[{
id: 1,
name: "project one ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2010"
},
{
id: 5,
name: "Priority",
value: "low"
},
]},
{
id: 2,
name: "project two ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2011"
},
{
id: 5,
name: "Priority",
value: "medium"
},
]},
{
id: 3,
name: "project three ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2012"
},
{
id: 5,
name: "Priority",
value: "high"
},
]}]
So if the params[:id] == 4 I want the list to be ordered by the custom_field id's corresponding value in decending order.
So in this case they would be ordered.
2012
2011
2010
Any ideas?
Is this what you are looking for? Your question is a bit unclear but I think this should suffice:
Your Original Hash:
test = [{
id: 1,
name: "project one ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2010"
},
{
id: 5,
name: "Priority",
value: "low"
},
]},
{
id: 2,
name: "project two ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2011"
},
{
id: 5,
name: "Priority",
value: "medium"
},
]},
{
id: 3,
name: "project three ",
custom_fields: [
{
id: 4,
name: "Year",
value: "2012"
},
{
id: 5,
name: "Priority",
value: "high"
},
]}]
Use group_by and sort(with handling for elements where there is no id found):
def group_and_sort(test_hash,id)
test_hash.group_by do |g|
elem = g[:custom_fields].detect {|h| h[:id] == id}
elem ? elem[:value] : "0"
end.sort.reverse.to_h
end
Then call like:
group_and_sort(test,4)
#=>{"2012"=>
[{:id=>3,
:name=>"project three ",
:custom_fields=>
[{:id=>4, :name=>"Year", :value=>"2012"},
{:id=>5, :name=>"Priority", :value=>"high"}]}],
"2011"=>
[{:id=>2,
:name=>"project two ",
:custom_fields=>
[{:id=>4, :name=>"Year", :value=>"2011"},
{:id=>5, :name=>"Priority", :value=>"medium"}]}],
"2010"=>
[{:id=>1,
:name=>"project one ",
:custom_fields=>
[{:id=>4, :name=>"Year", :value=>"2010"},
{:id=>5, :name=>"Priority", :value=>"low"}]}]}
Assume projects is the data you pasted.
def order_values_by_id(pjs, id)
pjs.map{|p| p[:custom_fields].find{|f| f[:id] == id}[:value] }.sort.reverse
end
order_values_by_id(projects)
#=> ["2012", "2011", "2010"]

Resources