Rails: Map array with values to model - ruby-on-rails

I have a problem. In my application I created the following migration for my model:
create_table(:calculation, primary_key: [:name, :value1]) do |t|
t.string :name
t.bigint :value1
t.decimal :value2
t.decimal :value3
t.decimal :value4
t.timestamps
end
From the API that I am connecting to, I get a response that looks like this:
[
[
44812342, // value1
"723.21000000", // value2
"723.21000000", // value3
"0", // UNNESSECARY VALUE (SKIP THIS ONE)
"723.21000000", // value4
],
[
44812342, // value1
"723.21000000", // value2
"723.21000000", // value3
"0", // UNNESSECARY VALUE (SKIP THIS ONE)
"723.21000000", // value4
]
]
Now I want to write a job which at the end writes all the received calculations to my postgres DB using bulk insert. Performance is really important, so I thought I should map it first to rails models and then call the insert_all and pass the array with all the models. The problem I am having is that the result does not contain any hashes. I thought of using something like this:
name = 'MyName'
calculationModels = calculationArrays.map do |calculation|
Calculation.new(
name: name,
value1: calculation[0],
value2: calculation[1],
value3: calculation[2],
value4: calculation[4]
)
end
Calculation.insert_all(calculationModels)
but I am not sure if this is fast to use, because in production it will do this for like over a million calculations, so my question is.. What is the fastest way to get these arrays into my database?

When you response from the API looks like this:
nested_values = [
[44812342, "723.21000000", "723.21000000", "0", "723.21000000"],
[44812342, "723.21000000", "723.21000000", "0", "723.21000000"]
]
then you can just input records into your database like this:
nested_values.each do |values|
Calculation.create!(
name: 'A NAME',
value1: values[0],
value2: values[1],
value3: values[2],
value4: values[4],
)
end
Or you can use insert_all like this:
Calculation.insert_all(
nested_values.map { |values|
{
name: 'A NAME',
value1: values[0],
value2: values[1],
value3: values[2],
value4: values[4]
}
}
)
Note that you will need to set a name in both cases, because you defined that column to be part of the primary key.

Related

rails array of hashes calculate one column

I have an array and it has many columns and I want to change one value of my one column.
My array is:
[
{
id: 1,
Districts: "Lakhisarai",
Area: 15.87,
Production: 67.77,
Productivity: 4271,
Year: 2015,
Area_Colour: "Red",
Production_Colour: "Orange",
Productivity_Colour: "Dark_Green",
created_at: "2018-07-24T11:24:13.000Z",
updated_at: "2018-07-24T11:24:13.000Z"
},
{
id: 29,
Districts: "Begusarai",
Area: 18.53,
Production: 29.35,
Productivity: 1584,
Year: 2015,
Area_Colour: "Red",
Production_Colour: "Red",
Productivity_Colour: "Orange",
created_at: "2018-07-24T11:24:13.000Z",
updated_at: "2018-07-24T11:24:13.000Z"
},
...
]
This is my sample array and I want my Productivity to be divided by 100 for that I am using one empty array and pushing these hashes to my array like:
j = []
b.map do |k|
if k.Productivity
u = k.Productivity/100
j.push({id: k.id, Productivity: u })
else
j.push({id: k.id, Productivity: k.Productivity })
end
Is there any simple way where I can generate this kind of array and reflect my changes to to one column. Is there any way where I don't need to push name of column one by one in push method.
I want to generate exact same array with one modification in productivity
let's say your array is e, then:
e.each { |item| item[:Productivity] = item[:Productivity]/100}
Example:
e = [{p: 12, d: 13}, {p:14, d:70}]
e.each { |item| item[:p] = item[:p]/10}
output: [{:p=>1, :d=>13}, {:p=>1, :d=>70}]
You could take help of map method here to create a new array from your original array, but with the mentioned changes.
ary.map do |elem|
h = elem.slice(:id)
h[:productivity] = elem[:Productivity] / 100 if elem[:Productivity]
h
end
=> [{:id=>1, :productivity=>42}, {:id=>29, :productivity=>15}]
Note, Hash#slice returns a new hash with only the key-value pairs for the keys passed in argument e.g. here, it returns { id: 1 } for first element.
Also, we are assigning the calculated productivity to the output only when it is set on original hash. Hence, the if condition there.

Rails translate enum in an array

I am currently working on statistics, so I get an array containing all my data. The problem is that this data contains enums and that I would like to translate them without overwriting the rest.
Here is a given example that contains my array (it contains several hundred) :
#<Infosheet id: 90, date: "2018-04-22 00:00:00", number: 7, way: "home", gender: "man", age: "age1", district: "", intercommunal: "", appointment: true, othertype: "", otherorientation: "", user_id: 3, created_at: "2018-04-22 17:51:16", updated_at: "2018-04-22 17:51:16", typerequest_id: 168, orientation_id: 188, info_number: nil, city_id: 105>
I would like to translate the enums of "way" or "gender" or "age", while retaining the rest of the data, because currently, if I make a translation in the console, it crushes everything else.
Do you know how to make that ?
Thanks !
You can just loop over all the enum attributes and get their values. Later you can merge and pass a new hash containing converted values
ENUM_COLUMNS = %i[way gender age] # Equivalent to [:way, :gender, :age]
def convert_enums
overrided_attributes = {}
ENUM_COLUMNS.each { |column| overrided_attributes[column.to_s] = self[column] }
attributes.merge(overrided_attributes)
end
NOTE:
While infosheet.gender returns you male or female
infosheet[:gender] will return you the respective integer value 0 or 1
You can test this if you use translate enum gem :
a = Infosheet.group(:gender).count
{“male”=>30, “female”=>6532}
Create a hash
r = Hash.new
And populate this with :
a.each {|s| puts r[Infosheet.translated_gender(s[0])]=s[1] }
r
result :
{“homme”=>30, “femme”=>6532}

Parsing JSON file in Rails: extracting and mapping values to DB model

I have a JSON file the has a structure something like this:
[
{
"Name" : {
"Attribute" : " Value",
"Attribute2" : " Value2",
"Attribute3" : " Value3",
}
, "Name2" : {
...
}
]
I'm trying to seed this file into a database table. I don't need all attribute:value pairs, so I need to map the ones I need to the create command in the loop. This is what I've attempted in the seeds.rb file:
json = ActiveSupport::JSON.decode(File.read("db/exercises.json"))
json.each_with_index do |e, index|
Model.create!(
name: e[0]
)
end
What I essentially need to do is something like this:
Model.create!(
name: e[0],
attribute1: e[0][attribute1],
attribute3: e[0][attribute3]
)
Any help ?
Most of your confusion is around how to access your objects. ActiveSupport::JSON.decode parses your data into an array containing a Hash with keys "Name", "Name2", etc. Hash#each yields pairs of |key, value| that you can use to populate your database. Your keys become Model#name, and then you can map the data's attributes to your model's.
data = ActiveSupport::JSON.decode(File.read("db/exercises.json"))
data = data[0] # Unwrap the Array
data.each do |name, attributes|
Model.create!(
name: name,
column_one: attributes['one'],
column_two: attributes['another one']
)
end
If your file shares key names with your columns, Rails provides Hash#slice to easily pull out a subset.
> {a: 1, b: 2, c: 3}.slice(:a, :c)
#=> {a: 1, c: 3}

Nested dynamic array rails

I have created an array
steps = [{'title' =>'abc','content' =>'click this', 'target' => 'bca'}]
tours = ['id'=>'tour', 'steps:' => "#{steps}"]
puts tours
Getting following output :
{"id"=>"tour", "steps:"=>"[{\"title\"=>\"abc\", \"content\"=>\"click this\", \"target\"=>\"bca\"}]"}
The structure of the output is right but i don't want these \ in the output.
What should i do to remove these \.
Thanks!
In ruby "#{}" invoke the to_s method on the object. You can check it run the following code: steps.to_s.
Just use:
tours = ['id'=>'tour', 'steps:' => steps]
Because this:
"[{\"title\"=>\"abc\", \"content\"=>\"click this\", \"target\"=>\"bca\"}]"
is a string representation of:
[{'title' =>'abc','content' =>'click this', 'target' => 'bca'}]
Зелёный has the direct answer for you, however, there's a more pressing issue I would point out -- I think you're getting confused between {hashes} and [arrays]
--
An array is a set of unordered data:
array = [3, 4, 5, 6, 0, 5, 3, "cat", "dog"]
Arrays are mainly used for non-sequential collections of data, a good example being product_ids in a shopping cart.
Arrays can only be identified by using the location of the data inside the array:
array[1] # -> 4
array[2] # -> 5
--
A hash is a collection of key:value pairs:
hash = {name: "Greg", type: "cat"}
Hashes are used when you wish to assign multiple values to a single piece of data, and can be called by referencing the "key" of the hash:
hash["name"] #-> Greg
hash["type"] #-> cat
Whilst you can create an array of hashes:
hash_array = [{name: "Greg", type: "cat"}, {name: "Sulla", type: "Dog"}]
... the problem with this is that you cannot call the hashes directly - they have to be through the array:
hash_array["name"] # -> error
hash_array[0]["name"] #-> "Greg"
Thus, I'd use the following in your example:
steps = {'title' =>'abc','content' =>'click this', 'target' => 'bca'}
tours = {id: 'tour', steps: steps}
tours.inspect #-> { id: "tour", steps: { "title" => "abc", "content" => "click this", "target" => "bca" }

Rails Limit results based on Date Range

I have the following Strucuture in the database
db.slots.find().pretty()
{
"_id" : ObjectId("52ae8990bd521b2da7000003"),
"created_at" : ISODate("2013-12-16T05:03:12.345Z"),
"day_from" : "Mon",
"day_to" : "Sat",
"doctor_clinic_id" : ObjectId("52ae8990bd521b2da7000004"),
"evening_time_from" : 0,
"evening_time_from_period" : "AM",
"evening_time_to" : 0,
"evening_time_to_period" : "AM",
"morning_time_from" : 9,
"morning_time_from_period" : "AM",
"morning_time_to" : 2,
"morning_time_to_period" : "PM",
"store" : [
ISODate("2013-12-13T09:00:00Z"),
ISODate("2013-12-13T09:15:00Z"),
ISODate("2013-12-13T09:30:00Z"),
ISODate("2013-12-13T09:45:00Z"),
ISODate("2013-12-13T10:00:00Z"),
ISODate("2013-12-13T10:15:00Z"),
ISODate("2013-12-13T10:30:00Z"),
ISODate("2013-12-13T10:45:00Z"),
ISODate("2013-12-13T11:00:00Z"),
ISODate("2013-12-13T11:15:00Z"),
ISODate("2013-12-13T11:30:00Z"),
ISODate("2013-12-13T11:45:00Z"),
ISODate("2013-12-13T12:00:00Z"),
ISODate("2013-12-13T12:15:00Z"),
ISODate("2013-12-13T12:30:00Z"),
ISODate("2013-12-13T12:45:00Z"),
ISODate("2013-12-13T13:00:00Z"),
ISODate("2013-12-13T13:15:00Z"),
ISODate("2013-12-13T13:30:00Z"),
ISODate("2013-12-13T13:45:00Z"),
........
.....
..
ISODate("2013-12-25T13:15:00Z"),
ISODate("2013-12-25T13:30:00Z"),
ISODate("2013-12-25T13:45:00Z"),
ISODate("2013-12-25T14:00:00Z")
],
"updated_at" : ISODate("2013-12-16T05:03:12.345Z")
}
I want to only fetch the content of the Store which has the date of Today and the next five days.
When i try the following i get a single slot back
slots.where(:store.gte => Date.today)
Results
#<Mongoid::Criteria selector: {"doctor_clinic_id"=>"52ae8990bd521b2da7000004", "store"=>{"$gte"=>Mon, 16 Dec 2013}} options: {} class: Slot embedded: false>
slot.rb
class Slot
include Mongoid::Document
field :store, type: Array
belongs_to :clinic
end
slot.store.where(:store.gte => Date.today) resulting one output like above..!!!
First: What you are trying to achieve is called projection in MongoDB, which is used when you want to retrieve only certain fields from an element. Indeed what you are trying to do is retrieve certain elements from an array field (See Mongodb projection positional)
In mongo you try something like this:
query={}
projection = {day_from:1, day_to:1} //Retrieve only _id, day_from and day_to fields
db.slots.find(query,projection).pretty()
Mongodb projection positional is what you would need but the documentation says that it only returns the first element that matches the query.
So I guess that you cannot directly with mongo. I recommend you to try a different approach like a formatter and virtual attributes. Something similar to:
class Slot
include Mongoid::Document
field :store, type: Array
def with_dates_after date
self.virtual_store= self.store.select{|elem| elem >= date}
end
def virtual_store= arg_arr
#_virtual_store = arg_arr
end
def virtual_store
#_virtual_store ||= []
end
end

Resources