rails array of hashes calculate one column - ruby-on-rails

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.

Related

Rail convert array into group

I am trying to convert one of my array into some format where it can convert itself into table format.
I have an array which is:
[
{
id: 1,
Revenue_Account: "Revenue Receipt",
Amount: 59567,
Year: "2012-13",
created_at: "2018-08-21T06:30:17.000Z",
updated_at: "2018-08-21T06:30:17.000Z"
},
{
id: 2,
Revenue_Account: "Revenue Expenditure ",
Amount: 54466,
Year: "2012-13",
created_at: "2018-08-21T06:30:17.000Z",
updated_at: "2018-08-21T06:30:17.000Z"
},
...
]
Full code of my array link to my actual array
I want this data to be converted into this format:
data: [
{
id: 1,
Sector: "Revenue Receipt",
2012-13: 59567,
2013-14: 68919,
2014-15: 72570,
2015-16: 96123,
2016-17: 105585,
2017-18_BE: 137158,
},
{
id: 2,
Sector: "Revenue Expenditure",
2012-13: 59567,
2013-14: 68919,
2014-15: 72570,
2015-16: 96123,
2016-17: 105585,
2017-18_BE: 137158,
},
....
]
I am using this code to group my array:
group = b.group_by{|data| data[:Revenue_Account]}
this is grouping my data as I am expecting in order to achieve my goal I am trying this code.
group = b.group_by{|data| data[:Revenue_Account]}
du = []
group.each do |i|
du.push({Sector:i[0]})
end
This is giving me Sector wise result how can I add year in my code.
You can't have a single id in there because you're grouping up many entries with different ids, but this is how you'd get the array in the format you're asking for:
grouped = {}
b.each do |x|
grouped[x[:Revenue_Account]] ||= {}
grouped[x[:Revenue_Account]][:Sector] = x[:Revenue_Account]
grouped[x[:Revenue_Account]][x[:Year]] = x[:Amount]
end
return {data: grouped.values}
Which gets you:
{
:data=>[
{
:Sector=>"Revenue Receipt",
"2012-13"=>59567,
"2013-14"=>68919,
"2014-15"=>78417,
"2015-16"=>96123,
"2016-17"=>105585,
"2017-18_BE"=>137158
},
{
:Sector=>"Revenue Expenditure ",
"2012-13"=>54466,
"2013-14"=>62477,
"2014-15"=>72570,
"2015-16"=>83616,
"2016-17"=>94765,
"2017-18_BE"=>122603
},
]
}
We build a new hash by looping through the original hash and creating hash keys if they don't exist. Then we start assigning values as you want them to be in the output. On each iteration, we're creating a new key in this hash for the Revenue_Account value if its the first time we've seen it. Then we assign that particular Revenue_Account's Date and Amount to the output. So for value 'Revenue Receipt' it looks like this:
Grouped hash starts off as empty
On first iteration, we see that group["Revenue Receipt"] is nil, so we initialize it with an empty hash via ||= (assign if nil)
We then assign :Sector => "Revenue Receipt" and this entry's Year and Amount, "2012-13" => 59567
Our grouped hash looks like: {"Revenue Receipt" => {:Sector => "Revenue Receipt", "2012-13" => 59567}
On the next iteration we see that group["Revenue Receipt"] is not nil, so ||= does not override it with an empty hash
We then assign :Sector => "Revenue Receipt" and this entry's Year and Amount, "2012-14" => 68919, which adds a new key/value to the existing hash
Our grouped hash now looks like: {"Revenue Receipt" => {:Sector => "Revenue Receipt", "2012-13" => 59567, "2012-14" => 68919}
After we parse the entire array, we now have a hash that has a key of the Revenue_Account, and values which look like the hash output you're expecting.
We discard the key and return only the hash values, which gets you the final output.
Another option, directly manipulating the array.
array_of_data = array
.each { |h| h[:Sector] = h.delete(:Revenue_Account) }
.each { |h| h[h[:Year]] = h[:Amount]}
.each { |h| h.delete_if{ |k, _| k == :created_at || k == :updated_at || k == :id || k == :Year || k == :Amount} }
.group_by { |h| h[:Sector] }
.values.map { |a| a.inject(:merge) }
Then just:
h = {}
h[:data] = array_of_data
To understand what happens along the code, just ad line by line outputting the result, like:
p array
.each { |h| h[:Sector] = h.delete(:Revenue_Account) }
Then:
p array
.each { |h| h[:Sector] = h.delete(:Revenue_Account) }
.each { |h| h[h[:Year]] = h[:Amount]}
Etcetera...
To understand .inject(:merge), see here Rails mapping array of hashes onto single hash

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}

How to merge 2 activerecord records together and be left with 1? Rails

I have 2 apples:
{
id: 1,
rotten: true,
branch_on_tree: nil,
type: "red delicious"
},
{
id: 2,
rotten: nil,
branch_on_tree: 5,
type: "red delicious"
}
They are duplicate apples for red delicious. How do I merge the records together and then delete the one with missing data? Is there a convenient way to do this?
Note: There might be like 10 duplicates. I don't want any null values in my final record. Non-null values take precedence.
Not very convinient way but it will work
assuming apples is an array:
[
{
id: 1,
rotten: true,
branch_on_tree: nil,
type: "red delicious"
},
# ...
]
that can come from:
apples = Apple.where(type: "red delicious")
apples_attrs = apples.map(&:attributes)
Then,
apple_attrs = apples_attrs.reduce do |apple, next_apple|
apple.merge(next_apple) do |_, old_value, new_value|
old_value || new_value
end
end
apples.destroy_all
Apple.create(apple_attrs)
You might want to check this guide https://apidock.com/ruby/Hash/merge
Assuming type always has some value, you can use DISTINCT with where clause. The below should work
Apple.where('rotten IS NOT NULL AND branch_on_tree IS NOT NULL').select('DISTINCT ON (type) rotten,branch_on_tree,type').take

How can I do something like this with ruby arrays

I have an user array like this:
users_array = [[1,text for 1],[2,text for 2],[3,text for 3],[4,text for 4],[5,text for 5]]
here first element is user_id and second element is text which is specific to user_id in the same array.
Now I am trying to have user object from instead of ids in array like these.
users_array = [[#<User id: 1, encrypted_email: "">,text for 1],[#<User id: 2, encrypted_email: "">,text for 2],[#<User id: 3, encrypted_email: "">,text for 3],[#<User id: 4, encrypted_email: "">,text for 4],[#<User id: 5, encrypted_email: "">,text for 5]]
I am trying not to loop the array and hit the db thousand times for thousands user.
data = users_array.to_h
# find all users with single query and build your map
User.where(id: data.keys).map { |user| [user, data[user.id]] }
You could use transpose to extract ids and values, and zip to combine users and values:
ids, values = users_array.transpose
users_array = User.find(ids).zip(values)

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" }

Resources