grouping children by parent attribute - ruby-on-rails

Here's what I am trying to achieve:
Group_x.name
member1.name -- member1.join_date -- etc
member2.name -- member2.join_date -- etc
...
Group_y.name
member1.name -- member1.join_date -- etc
member2.name -- member2.join_date -- etc
...
What I'm going for is really very similar to this although the implementation there doesn't work for me.
I've gotten this far in my controller:
def index
# https://stackoverflow.com/a/17835000/2128691
#user_group_ids = current_user.student_groups.map(&:id)
#students = Student.where('student_group_id IN (?)', #user_group_ids)
# https://stackoverflow.com/a/10083791/2128691
#students_by_group = #students.uniq {|s| s.student_group_id}
#title = "All students"
end
and calling the following in my view -
<% #students_by_group.all.each do |x| %>
<p>
<%= "#{x}" %>
</p>
<% end %>
gives me a list of all student objects. if i call <%= "#{x.name}" %> or <%= "#{x.created_at}" %>, etc, I get the correct information, and everything is great.
But now that I have all this information, how can I put the group.name (in my code it would be x.student_group.name) as a header for all of the students for which that group_name is true?

I think you need to use group_by on #students_by_group like this:
#students_by_group = #students_by_group.group_by { |s| s.student_group }
This would return a hash with the keys being the student group objects and the values being the students that belongs to this group, then you can do this in your view:
<% #students_by_group.each do |group, students| %>
<h3><%= group.name %></h3>
<% students.each do |x| %>
<p>
<%= "#{x}" %>
</p>
<% end %>
<% end %>
As an additional note, the group_by would fire a query for each student, so you may want to eagerly load the student group for each student like this for some performance gain:
#students = Student.where('student_group_id IN (?)', #user_group_ids).includes(:student_group)

Related

How to show unique strings in Rails?

I want to categorise Companies by letters in my listing page.
But there are multiple records that start with the same letter. I can get the first letters of Company names with the .first method.
But my code shows duplicate letters.
Controller:
#brand = Brand.where(slug: params[:brand]).first
#companies = Company.where(brand_id: #brand.id)....
View:
<% #companies.each do |c| %>
<li>
<%= link_to company_path(c.brand.name,c.id) do %>
<%= c.name.first %>
<% end %>
</li>
<% end %>
How would I show only unique letters?
New code (with some changes, assuming company belongs_to brand and brand has_many companies)
#brand = Brand.where(slug: params[:brand]).first
#companies = #brand.companies
##list is a hash with letters as keys and an array of companies starting with this letter
#list = Hash.new { |h,k| h[k]=[] }
#companies.each { |comp| #list[comp.name[0]] << comp }
#As “engineersmnky” suggested on a comment the last two sentences can be simplified:
#list = #companies.group_by { |c| c.name[0] }
View:
<% #list.each do |letter, companies| %>
<h2><%= letter %><h2>
<% companies.each do |c| %>
<li>
<%= link_to c.name, c %>
</li>
<% end %>
<% end %>
You're iterating through all companies so if you have companies like, Facebook and Foxtel, a link with F tag is going to be duplicated. You can do something like
categories = #companies.where(brand_id: #brand.id).pluck(:name).map(&:first).uniq
In the above code all it is doing is getting a list of company names and extracting out the first letter and removing the duplicates.
Now you can iterate through the categories and create a link for the category and inside the category page you can get companies starting from that letter
Company.where('name LIKE ?', "%#{starting_character}%")
starting_character gets sent from the list of categories page.

How to use include or join into with a each method

I need a little advice about the join and includes methods.
I display a list of groups in the index view. Each has a modal associated, and, in this modal, I would like to display the requests associated to this group. Normally, I'd use #requests = group.requests, but would like to use join for sending just one request to my database.
Since I'm in the index view, I don't have a #group in my action.
controller:
def index
#groups = current_user.groups
end
view (index):
<% #groups.each do |g| %>
<MODAL>
<% #requests = g.requests %>
<% #requests.each do |r| %>
<%= r.date %>
<% end %>
</MODAL>
<% end %>
I guess I can also use join and include for #groups, but there is already one SQL request, so I'm good with it.
In your controller, add includes like this to preload requests and avoid n+1 queries.
def index
#groups = current_user.groups.includes(:requests)
end
View is fine, but you can also write as:-
<% #groups.each do |g| %>
<MODAL>
<% g.requests.each do |r| %>
<%= r.date %>
<% end %>
</MODAL>
<% end %>

Rails loop multiple instance variables on same view

Let say I have a controller Transactions:
#transactions = Transaction.all.group(:type)
#transaction_date_asc = Transaction.all.order(:DATE => :desc).group(:type)
#transaction_date_desc = Transaction.all.order(:DATE => :asc).group(:type)
In my view I need to loop all 3 instances.
Something like this where I want to show newest and oldest amount or discount for each transaction type.
<% #transactions do |transaction|%>
transaction.type.name
<%end%>
<% #transaction_date_asc do |transaction_asc|%>
transaction_asc.amount
<%end%>
<% #transaction_date_desc do |transaction_desc|%>
transaction_desc.amount
<%end%>
<% #transaction_date_asc do |transaction_asc|%>
transaction_asc.discount
<%end%>
<% #transaction_date_desc do |transaction_desc|%>
transaction_desc.discount
<%end%>
How am I supposed to place loops, columns and <%end> in my view?
Someone may come up with a better solution, but from my seat it looks like you need to do a query for each group in your view to get the first date transaction, but on the plus side, you only need one query in the controller for the transactions with the last date.
#transactions = Transaction.group(:type).having('DATE = MAX(DATE)')
In the view...
<% #transactions do |transaction| %>
<% first_transaction = Transaction.where(type: transaction.type).order('transaction_date').first %>
<%= transaction.type.name %>
<%= first_transaction.amount %>
<%= transaction.amount %>
<%= first.transaction.discount %>
<%= transaction.discount %>
<% end %>
However... to keep the logic in the view cleaner you could have an instance method for transaction types that will return the first and last transaction.
class Type << ActiveRecord::Base
def first_transaction
Transaction.where(type: self).order('transaction_date ASC').first
end
def last_transaction
Transaction.where(type: self).order('transaction_date DESC').first
end
end
Then in the controller...
#types = Type.all
then in the view...
<% #types.each do |type| %>
<%= type.name %>
<%= type.first_transaction.try(:amount) %>
<%= type.last_transaction.try(:amount) %>
<%= type.first_transaction.try(:discount) %>
<%= type.last_transaction.try(:discount) %>
<% end %>
The reason I'm suggesting #try is to handle the case of no transactions being present for a specific type.

Display title instead of id from search results

I have spent hours on this single problem, I am desperate for help.
The below html displays the correct car name and the car.manufacture_id displays the correct manufacture id, except I need to display manufacture.name and can not figure out how to do that. How do I display car.manufacture.name?
search.html.erb
<% #cars.each do |car| %>
<%= car.manufacture_id %>
<%= link_to car.name, manufacture_path(car.manufacture_id) %>
<% end %>
search_controller.rb
def search
if params[:q].nil?
#cars = []
#manufactures = []
else
#cars = Car.search params[:q]
#manufactures = Manufacture.search params[:q]
end
end
What do your car and manufacture models looks like, can you show the code for them? The schemas would help too. If your models are setup right car.manufacture.name should have worked.
If you do
<% #manufactures.each do |manufacture| %>
<%= manufacture.id %>
<%= manufacture.name %>
<% end %>
Does this work?
Also this might work, if the manufacturers are being found by Manufacture.search params[:q]
<%= #manufactures.find(car.manufacture_id).name %>
or if they are not
<%= Manufacture.find(car.manufacture_id).name %>

Rails: Multiple models records list

How can I show recent added #post and #photos in one list? For example:
post - LAlala (10.10.2011)
photos - [] [] [] [] (1.1.2011)
post - Bbbdsfbs (2.12.2010)
post - Lasdasdf2 (2.10.2009)
#posts = Post.limit(20).order('created_at desc')
#photos = Photo.limit(20).order('created_at desc')
#recent_items = (#posts + #photos).sort_by(&:created_at)
<% #recent_items.each do |item| %>
<% if item.class == "Photo" %>
<%= image_tag item.url %>
<% else %>
<%= item.title %>
<% end %>
<% end %>
Alternatively, use group_by to do it like this:
#recent_items = (#posts + #photos).group_by(&:created_at)
<% #recent_items.each do |date, items| %>
Date: <%= date %>
<% items.each do |item| %>
Show information here.
<% end %>
<% end >
I would move the view logic into a helper if I were you for DRYer code.
It is much better to do this is the database.
I just say this: polymorphism + database views.
Create a database view which contains the columns you need from both Post and Photo, including the column "type" containing a the name of the model (you need it for the polymorphism). Call this view for example "list_items". Then create a model called "ListItem". Then you can use this model like any other, paginate it and whatever you need to do.
ListItem.order("created_at > ?", Date.yesterday).page(params[:page])
And don't forget to configure the polymorphic association
However, all this is much easier to accomplish with the listable gem. Check it out!

Resources