SQL using group_by to count users - ruby-on-rails

users = [
{"id":1, "zipcode":"10031"},
{"id":4, "zipcode":"10000"},
{"id":2, "zipcode":"10031"},
{"id":3, "zipcode":"10031"}
]
Hello guys. I need help a that can reduce the process of my code.
I have a users stored datas.
Were i want to achieve this in my first line of code
[
{"id":1, "zipcode":"10000", "users_count": 1},
{"id":2, "zipcode":"10031", "users_count": 3},
]
My code:
user = User.select(:zipcode).group(:zipcode).count(:id)
result of the above code is {"10000"=>1, "10031"=>3}
so i need to get the separation of keys and values
keys = user.keys
values = user.values
make a loop
i = 0
num = keys.length.to_i
zipcodes = []
while i < num do
zipcode = keys[i]
users_count = values[i]
zipcodes[i]= zipcode , users_count
i +=1
end
result if the above code is [[10000, 1], [10031, 3]]
I want to change the result if this code
user = User.select(:zipcode).group(:zipcode).count(:id)
from this
{"10000"=>1, "10031"=>3}
to this
[{"id":1, "zipcode":"10000", "users_count": 1},{"id":2, "zipcode":"10031", "users_count": 3}]
Thank you for any help.

You can modify your query as such,
user_counts = User.select(:zipcode, 'count(*) as user_count').group(:zipcode)
Please note that in the result that is printed on the console, the user_count will not be displayed, as it is not an attribute of the user model. However it is there, and can be accessed via,
user_counts.each do |user_count|
puts user_count.zip_code
puts user_count['user_count']
end
You can also view all the results by converting them to JSON
user_counts = User.select(:zipcode, 'count(*) as user_count').group(:zipcode)
puts user_counts.as_json

Related

How to slice individual columns (with values) in an ActiveRecord::Result in Rails?

I have a Rails query which is shown below:
query_results =
User.
joins("INNER JOIN posts ON posts.user_id = users.user_id").
select("posts.topic, posts.thread_id")
query_results contains values of 2 columns: topic and thread_id.
I would like to split query_results into 2 arrays - 1 containing values from all records (from query_results) for column topic alone and the 2nd containing values from all records for column thread_id alone.
How can I achieve this?
Try This out can help you!
here we are going to use pluck.
Yes. According to Rails guides, pluck directly converts a database result into an array, without constructing ActiveRecord objects. This means better performance for a large or often-running query.
topic_arr = []
thread_id = []
query_results = User.joins("INNER JOIN posts ON posts.user_id = users.user_id").pluck("posts.topic, posts.thread_id")
query_results.each do |i|
topic_arr.push(i.first)
thread_id.push(i.last)
end
puts query_results #=>[["topic1", 1], ["topic2", 2], ["topic3", 3]]
puts topic_arr #=>["topic1","topic2","topic3"]
puts thread_id #=>[1,2,3]
I think you can try below code for your requirement :-
query_results =
User.
joins("INNER JOIN posts ON posts.user_id = users.user_id").
pluck("posts.topic, posts.thread_id").to_h
topic_arr = query_results.keys
thread_id_arr = query_results.values
Example
Above query will give you result like:-
query_results = {"topic 1"=>1, "topic 2" => 2}
topic_arr = query_results.keys
topic_arr = ["topic 1", "topic 2"]
thread_id_arr = query_results.values
thread_id_arr = [1, 2]

Updating Ruby Hash Values with Array Values

I've created the following hash keys with values parsed from PDF into array:
columns = ["Sen", "P-Hire#", "Emp#", "DOH", "Last", "First"]
h = Hash[columns.map.with_index.to_h]
=> {"Sen"=>0, "P-Hire#"=>1, "Emp#"=>2, "DOH"=>3, "Last"=>4, "First"=>5}
Now I want to update the value of each key with 6 equivalent values from another parsed data array:
rows = list.text.scan(/^.+/)
row = rows[0].tr(',', '')
#data = row.split
=> ["2", "6", "239", "05/05/67", "Harp", "Erin"]
I can iterate over #data in the view and it will list each of the 6 values. When I try to do the same in the controller it sets the same value to each key:
data.each do |e|
h.update(h){|key,v1| (e) }
end
=>
{"Sen"=>"Harper", "P-Hire#"=>"Harper", "Emp#"=>"Harper", "DOH"=>"Harper", "Last"=>"Harper", "First"=>"Harper"
So it's setting the value of each key to the last value of the looped array...
I would just do:
h.keys.zip(#data).to_h
If the only purpose of h is as an interim step getting to the result, you can dispense with it and do:
columns.zip(#data).to_h
There are several ways to solve this problem but a more direct and straight forward way would be:
columns = ["Sen", "P-Hire#", "Emp#", "DOH", "Last", "First"]
...
#data = row.split
h = Hash.new
columns.each_with_index do |column, index|
h[column] = #data[index]
end
Another way:
h.each do |key, index|
h[key] = #data[index]
end
Like I said, there are several ways of solving the issue and the best is always going to depend on what you're trying to achieve.

Group records for data analysis in Rails

I have two tables connected with habtm relation (through a table).
Table1
id : integer
name: string
Table2
id : integer
name: string
Table3
id : integer
table1_id: integer
table2_id: integer
I need to group Table1 records by simmilar records from Table2. Example:
userx = Table1.create()
user1.table2_ids = 3, 14, 15
user2.table2_ids = 3, 14, 15, 16
user3.table2_ids = 3, 14, 16
user4.table2_ids = 2, 5, 7
user5.table2_ids = 3, 5
Result of grouping that I want is something like
=> [ [ [1,2], [3, 14, 15] ], [ [2,3], [3,14, 16] ], [ [ 1, 2, 3, 5], [3] ] ]
Where first array is an user ids second is table2_ids.
I there any possible SQL solution or I need to create some kind of algorithm ?
Updated:
Ok, I have a code that is working like I've said. Maybe someone who can help me will find it useful to understand my idea.
def self.compare
hash = {}
Table1.find_each do |table_record|
Table1.find_each do |another_table_record|
if table_record != another_table_record
results = table_record.table2_ids & another_table_record.table2_ids
hash["#{table_record.id}_#{another_table_record.id}"] = results if !results.empty?
end
end
end
#hash = hash.delete_if{|k,v| v.empty?}
hash.sort_by{|k,v| v.count}.to_h
end
But I can bet that you can imagine how long does it takes to show me an output. For my 500 Table1 records it's something near 1-2 minutes. If I will have more, time will be increased in progression, so I need some elegant solution or SQL query.
Table1.find_each do |table_record|
Table1.find_each do |another_table_record|
...
Above codes have performance issue that you have to query database N*N times, which could be optimized down to one single query.
# Query table3, constructing the data useful to us
# { table1_id: [table2_ids], ... }
records = Table3.all.group_by { |t| t.table1_id }.map { |t1_id, t3_records|
[t1_id, t3_records.map(&:table2_id)]
}.to_h
Then you could do exactly the same thing to records to get the final result hash.
UPDATE:
#AKovtunov You miss understood me. My code is the first step. With records, which have {t1_id: t2_ids} hash, you could do sth like this:
hash = {}
records.each do |t1_id, t2_ids|
records.each do |tt1_id, tt2_ids|
if t1_id != tt1_id
inter = t2_ids & tt2_ids
hash["#{t1_id}_#{tt1_id}"] = inter if !inter.empty?
end
end
end

Ruby on Rails Query Result Group

In my controller I have
def services
#services = WorkQuote.group(:work_type_id).count
end
Which when ran in IRB:
irb(main):016:0> #services = WorkQuote.group(:work_type_id).count
(0.7ms) SELECT COUNT(*) AS count_all, work_type_id AS work_type_id FROM "work_quotes" GROUP BY "work_quotes"."work_type_id"
=> {1=>2, 3=>1}
Notice that the result is {1=>2, 3=>1}. In my controller I want to make a loop that builds a new array where the number to the left of the => is resolved by being inserted into this WorkType.find(number to the left of =>).worktype
So the new array would look like [Install=>2, Repair=>1} assuming that WorkType.find(1).worktype = Install and WorkType.find(3).worktype = Repair
Here is what worked for me although I am sure there is a cleaner way of doing this.
Basically grabbed each value with .each_key and pushed it into an array. Next I made a loop to grab the "count" which is the right side of each hash. Also in the loop I made an array that combined the resolved name of the WorkType id and the "count".
I realize that #printcount isn't needed in this situation.
#services = WorkQuote.group(:work_type_id).count
#printservices = []
#services.each_key {|key| #printservices.push(WorkType.find(key).worktype) }
#services = #services.to_a
#printcount = []
#printdisplay = []
iz = 0
while iz < #services.size
#printcount[iz] = #services[iz][1]
#printdisplay[iz] = [#printservices[iz], #services[iz][1]]
iz = iz + 1
end

There has got to be a cleaner way to do this

I have this code here and it works but there has to be a better way.....i need two arrays that look like this
[
{
"Vector Arena - Auckland Central, New Zealand" => {
"2010-10-10" => [
"Enter Sandman",
"Unforgiven",
"And justice for all"
]
}
},
{
"Brisbane Entertainment Centre - Brisbane Qld, Austr..." => {
"2010-10-11" => [
"Enter Sandman"
]
}
}
]
one for the past and one for the upcoming...the problem i have is i am repeating myself and though it works i want to clean it up ...here is my data
..
Try this:
h = Hash.new {|h1, k1| h1[k1] = Hash.new{|h2, k2| h2[k2] = []}}
result, today = [ h, h.dup], Date.today
Request.find_all_by_artist("Metallica",
:select => "DISTINCT venue, showdate, LOWER(song) AS song"
).each do |req|
idx = req.showdate < today ? 0 : 1
result[idx][req.venue][req.showdate] << req.song.titlecase
end
Note 1
In the first line I am initializing an hash of hashes. The outer hash creates the inner hash when a non existent key is accessed. An excerpt from Ruby Hash documentation:
If this hash is subsequently accessed by a key that doesn‘t correspond to a hash
entry, the block will be called with the hash object and the key, and should
return the default value. It is the block‘s responsibility to store the value in
the hash if required.
The inner hash creates and empty array when the non existent date is accessed.
E.g: Construct an hash containing of content as values and date as keys:
Without a default block:
h = {}
list.each do |data|
h[data.date] = [] unless h[data.date]
h[data.date] << data.content
end
With a default block
h = Hash.new{|h, k| h[k] = []}
list.each do |data|
h[data.date] << data.content
end
Second line simply creates an array with two items to hold the past and future data. Since both past and the present stores the data as Hash of Hash of Array, I simply duplicate the value.
Second line can also be written as
result = [ h, h.dup]
today = Date.today

Resources