Grouping records on a show page - ruby-on-rails

In my Rails app, I have a Runs model, a Timesheets model, and an Athletes model. A Run belongs_to an Athlete, and an Athlete has_many Runs. A Run also belongs_to a Timesheet, and a Timesheet has_many Runs.
Timesheet/show/:id lists the runs associated with a particular timesheet, along with the athlete associated with each respective run.
On this page, it's common to have something like this:
Joe Smith 5.98, 14.98, 21.22, 36.33, 55.67
Jane Doe 4.99, 13.99, 21.22, 36.21, 55.66
Joe Smith 6.00, 14.87, 21.22, 36.31, 55.58
I'm sure this is a beginners mistake, but I don't know how to group the runs by each athlete, such as this:
Joe Smith
5.98, 14.98, 21.22, 36.33, 55.67
6.00, 14.87, 21.22, 36.31, 55.58
Jane Doe
4.99, 13.99, 21.22, 36.21, 55.66
What am I missing?

The awesome group_by method on any enumerable!
so if you have #timesheets:
#timesheets.group_by(&runner_id).each do |runner_id, runs|
runs.each{|run| puts run}
end

Related

Order scope results by closest match

I have three models: Doctor, Appointment, Patient. A Doctor has many patients through appointments and a Patient has many doctors, also through appointments.
I have a search query where you can filter doctors by patients, so if you input the names of the patients it will show you any doctor that has assigned any of those patients.
Here is the scope I use to search within the Doctor model:
scope :by_patient, lambda { |*names|
names_array = names.map { |name| "%#{name}%" }
joins(appointments: :patient).where('lower(patients.name) LIKE ANY (array[?])', names_array)
}
E.g.
Dr. Smith -> Patients: Karen, Joe, Kate, Mary
Dr. Johnson -> Patients: Kate, Mary
Dr. Spears -> Patients: John
If you look for Patients Kate and Mary the query will return both
Dr. Smith and Dr. Johnson, however, Dr. Smith should be shown first
because he has both patients while Dr. Johnson only has one.
How could I order the search results by those doctors who have the most patients?

combine 2 objects and sort rails 5

I want to show a time link mixing comments and post so I have this objects
#posts = Post::all()
#comments = Comment::all()
If I do this
#post.each ...
... end
#comments.each ...
... end
I will get first posts and after this, the comments. But I want a timeline, how i can create this?
I need to combine both object to create just one ordered list, example:
In post:
id | name | date
1 | post1 | 2015-01-01
2 | post2 | 2013-01-01
In comments:
id | name | date
1 | comment1 | 2014-01-01
2 | comment2 | 2016-01-01
if I do this
post.each ...
comments.each ...
the result will that:
-post1
-post2
-comment1
-comment2
But i need order by date to get
-post2
-comment1
-post1
-comment2
Thanks, and sorry for my ugly english.
Posts and comments are different models (and different tables), so we can't write SQL to get sorted collection, with pagination etc.
Usually I use next approach when I need mixed timeline.
I have TimelineItem model with source_id, source_type and timeline_at fields.
class TimelineItem < ApplicationRecord
belongs_to :source, polymorphic: true
end
Then I add in models logic to create timeline_item instance when needed:
has_many :timeline_items, as: :source
after_create :add_to_timeline
def add_to_timeline
timeline_items.create timeline_at: created_at
end
Then search and output as simple as
TimelineItem.includes(:source).order(:timeline_at).each { |t| pp t.source }
Solution#1
You can achieve this using UNION query.
sql= 'SELECT id, name, date FROM posts UNION ALL SELECT id, name, date FROM comments ORDER BY date'
ActiveRecord::Base.connection.execute(sql)
but union query will only work when you have same column names in both tables.
Solution#2 Add ordering logic in your view.
If you are simply displaying these records on html page then let them load on page without any specific order i.e.(first posts and then comments). Write javascript code to sort DOM elements which will run after page load.
for ref: Jquery - sort DIV's by innerHTML of children
Solution#3 Refactor your DB schema and put posts and comments in same database table. Then you will be able to query on single table.
Something like this,
class Text < ActiveRecord::Base
end
class Post < Text
end
class Comment < Text
end
Query will be Text.order(:date)
Refactoring your DB schema is too much to solve this problem. Do it if it makes sense for your application.

Many-to-many data model puzzle

Given
3 ActiveRecord models:
class Dealer < ActiveRecord::Base
end
class CarMake < ActiveRecord::Base
has_many :car_models
end
class CarModel < ActiveRecord::Base
belongs_to :car_make
end
non of either CarMake or CarModel should have additional foregin keys (making managing of makes/models isolated and independent),
adding join tables or associations is not prohibited and is welcome.
Problem
I need dealer to have assigned a desired subset of available car_makes and desired subset of car_models for each of respectively assigned car_make.
Example
Given this data:
car_models car_makes
------------------------ -------------
id car_make_id title id title
1 1 Flex 1 Ford
2 1 Fiesta 2 Chevrolet
3 1 Focus 3 Mercury
4 2 Impala 4 Nissan
5 2 Suburan
6 3 Milan
7 4 Altima
What I want is to do:
dealer1.his_makes # => [Ford, Chevrolet, Mercury]
dealer1.his_models # => [Flex, Fiesta, Impala, Milan]
dealer2.his_makes # => [Ford, Mercury, Nissan]
dealer2.his_models # => [Fiesta, Focus, Altima]
My question is:
Which associations/tables should I add to achieve this?.
Add an Inventory model that belongs to Dealer, CarModel, and CarMake. Toss in a 'quantity' field just for fun.
You could argue that CarModel isn't necessary, but if it's a common query, seems like a reasonable spot to de-normalize.
A join table between dealer and car makes would allow you to specify "desired car makes by dealer". If all the desired models were of the make specified in this table then I'd create a join table between DealerCarMakes and CarModels to specify the desired models for that make for that dealer.
While you could use a single table, there would be some issues:
You'd need a distinct on the query that retrieves desirable car makes, which often indicates a lack of normalisation.
Being able to specify a desirable car make would be dependent on there being a desirable car model for that make -- maybe not an issue here, but definitely an issue in other cases where there is not such a dependency.
You could not have attributes at the DealerCarMake level, such as "desirable since date" or a value range.

See if one person is before another in the alphabet, ruby, rails

I'm doing an app for a membership database.
Each person may have a partner. When it comes to displaying the list, I only want to have one row for each family, so at the moment I'm comparing first names and not displaying the row if the person's name is second. Like this
person.first_name != [person.first_name, person.partner.first_name].sort[0]
This means each family only gets displayed once, not twice - once for each partner.
And I'm doing this in the view.
There must be a better way of doing this, and it'd be really great if I could do it at the database level. I'm using postgresql if that makes a difference.
Edit
Sorry if it was unclear.
Say Person 1 has the first_name "Edward" and Person 2 has the first_name "Fay". Edward and Fay are married.
I only want to show them once in my list - I want a row to look like this
Surname First name Address etc
Mysurname Edward ....
Fay
I don't want to display it again with Fay first because I've got both Fay and Edward in list of people, so I use the ruby in the first part of the question to check if I should display the row - it compares their first names and only does the row if the person has a fist name that's before his/her partner's first name.
Here's the relevant part of my person model
class Person < ActiveRecord::Base
has_one :relationship_link, :foreign_key => :person_id, :dependent => :destroy, :include => :partner
has_one :partner, :through => :relationship_link, :source => :person_b, :class_name => "Person"
I hope that's clearer
You need to use DISTINCT ON or GROUP BY. In postgres you need to be careful to group by everything that you are selecting. If you only need to get the last names you can select("DISTINCT ON(last_name) last_name").pluck("last_name"). You will only get an array of last names though.
Maybe you can get records if you order by every other fields in your table, like this:
select("DISTINCT ON(people.last_name) people.*").order("people.last_name ASC, people.first_name ASC, people.field2 DESC, people.field3 ASC...")
You need to order by every attribute so the result is not ambigious.
For this case, i would create a data structure (a Hash) to store people instances given a specific surname. Something like this:
def build_surnames_hash(people_array)
surnames_hash = {}
people_array.each do |person|
last_name = person.last_name
surnames_hash[last_name] ||= []
surnames_hash[last_name] << person
end
surnames_hash
end
That way, you can iterate over the hash and display people using their surnames stored as hash's keys:
surnames_hash = build_surnames_hash(Person.all)
surnames_hash.each do |surname, person_instances_array|
# display the surname once
# iterate over person_instances_array displaying their properties
end

Rails, two dimensional table, pivot, nested hash loops

I am building grade-book report - a two dimensional table that shows lesson names going horizontally and a list of students going vertically.
Student Name | LessonID x | LessonID x | LessonID x
Joe 95% 95%
Mary 80% 80%
Sam 80% 80%
My data is in a table that has these fields:
student_id, lesson_id, grade_in_pct, grade_in_pts, grade_high, grade_low, grade_median
The total number of students and lessons is not fixed.
I considered using ruport/acts_as_reportable or mysql pivot procedure, however it looks like the pivot only gives me one dimension. So that's not going to work, because in my view I want to add mouse-over features and conditional formatting to show more info on each grade.
So I think my only option is to generate a nested hash and then loop through it in the view. What are your thoughts? Could someone suggest a way to build a nested hash? Would it be too processor intensive to loop through 250 rows (~50 students, 5 lessons each)?
I am stuck. Please help. Thanks!
This is how I would do it:
MODELS:
Student Model:
has_many: Grades
has_and_belongs_to_many: Lessons
Lesson Model:
has_many: Grades
has_and_belongs_to_many: Students
Grade Model:
belongs_to: Student, Lesson
CONTROLLER:
#data = Student.all
#lessons = Lesson.all
VIEW:
header_row
#data.each do |student|
#lessons.each do |lesson|
student.grades.find_by_lesson(lesson).some_data

Resources