Show only unique results with collection_select - ruby-on-rails

I have a series of reviews for a professor, I want users to be able to sort reviews based on the class taught. I have a collection_select but I'm having trouble filtering out duplicates, so for example if more than one user had a professor for "English" you will just see "English" twice in the list.
This is what I have:
<%= collection_select(:professor, :id, Review.where(professor_id: #professor.id), :id, :whatclass) %>
I've seen some solutions saying that I should add .uniq but that dosen't seem to do anything.
<%= collection_select(:professor, :id, Review.where(professor_id: #professor.id).uniq, :id, :whatclass) %> I'm pretty sure this is not working because it's referring to the Review.id rather than Review.whatclass

Your list contains duplicates because it has an entry for every review, not for every class taught, which is not unique for each review. I am not even sure you need to use collection_select to render a simple list of unique class names.
In the controller, try:
#unique_classes = Review.
where(professor_id: #professor.id).
pluck(:whatclass).uniq
Here, pluck returns an array of just the whatclass attributes of every review of the professor. Calling uniq on that will drop any duplicates in the array. If you have lots of reviews for each professor, you can get better performance by having the database filter the duplicates:
#unique_classes = Review.
where(professor_id: #professor.id).
pluck("DISTINCT whatclass")
This will generate a SELECT DISTINCT query, so the array pluck returns will already be unique.
Finally, you can plug this array of whatclass values into a simple select_tag:
select_tag :class_name, options_for_select(#unique_classes)
The code above is untested, so it might need some changes to work.

Related

How can I set a fallback ordering system?

I'm printing a list, ordered as such:
<% #users.order(:number).each do |u| %>
<%= u.name %>
<% end %>
But some users will have the same value of :number, and in that case the records appear to be ordered based on time_created. How can I set a fallback or secondary ordering system to be used when records have the same value of the attribute used to order them?
You can just keep adding on to the order method.
#users.order(:number, :name, :last_logged_in, :id)
If the numbers are the same, it will break the tie with name, then last_logged_in, then id. This is all done in the database for you.
If you already had the data in memory, then you could use Ruby's sort_by method.
You're probably going to want to use sort_by instead of order.
#users.sort_by {|u| [u.number, u.second_option] }
that will sort first by number and then if number matches it will pull whatever the second option is and compare by that. Technically you can throw as many things to sort by as you want into that array.
Edit: Actually, there's a similar question already answered here

How to show text instead of integers?

I have a form with a pull down list of Races (asian, caucasian, African/black, etc).
These races are represented by my Race model. I can create a record successfully, however, when I try to view the record the corresponding integer of the race is displayed and not the text.
How do I get the text associated instead of the integer id?
Thanks.
Provided you supply a minimum set of data for us to assist, I risk an answer as generically as I can.
If you are using a form_for builder
<%= f.collection_select :race_id, Race.all, :id, :name %>
or whatever you call your fields
If you are using a form_tag
<%= select_tag :race_id, options_from_collection_for_select(Race.all, "id", "name") %>
These are as I said pretty generic answers, you can build further on them. Check this link for more:
http://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html
You are seeing the value of the selected option which is the index of the array I imagine. You can use that index to get the text value from your races array. Without seeing more code that is all I can provide.

How to convert an integer column in the model to be used as a string in the view

For database columns that are integers that "represent" strings, what is the best way to show the string value in the view?
For example, if I collect "payment_method" values as integers in my form as follows:
<%= f.select :payment_method, { "Visa" => "1", "Mastercard" => "2", "Amex" => "3"} %>
How can I show the saved integer as a string in my view files? What can I add to my model, so that
<%= #relevantvariable.payment_method %>
or something similar returns string values like "Visa", "Mastercard" or "Amex" instead of their respective integer values?
Thanks much for handling such a basic question!
Either don't use an integer value, and store the payment method directly as a string in the db, or create a PaymentMethod model.
With the association set up, you'd be able to refer to the name of the payment method as so:
<%= #relevantvariable.payment_method.name %>
Don't try to manually handle lists of names / ids - that will quickly get unmanageable.
Edit: after reading your comment, if you went with the first option and stored a string in the table, definitely don't allow the user to type the value directly, maintain a list on the model (or similar), that you seed your dropdown from, that way you're still constraining the possible values. You could even add a custom validator if you want to be certain what you putting in the database.
I'd stick with cheeseweasel solution but you can do one thing to show that on your view...
<% if #relevantvariable.payment_method == 1 %>
<%= "Visa" %>
<% else %>
<%= "Mastercard" %>
You probably would want to use a switch/case instead but you got the idea
As I said I think you should stick with cheeseweasel solution since there are many problems with this approach... it's your call
So you have your payment methods in a separate table payment_methods and the owner ( say user) contains a has_one relationship with it
class User < AR::Base
has_one :payment_method
end
Then show the payment method name just like
<%=
#user.payment_method.name #or whatever you have.
%>
However, while you are loading the #user data, you can perform a eager loading by :include. Like
User.find(user_id, :include => :payment_method)
This will load the PaymentMethod data earlier even for multiple users with single query.

List all sub categories in collection select

Building a form for users to submit data. I can't seem to understand or figure out how *collection_select* method works.
Essentially I'm trying to give my users the option to choose which sub_category their product belongs in when submitting the form. What should the collection_select syntax look like?
I ended up figuring it out based on comments, here is what I ended up with, in case it helps anyone else.
<%= f.collection_select(:sub_category_id , SubCategory.find(:all), :id , :name) %>
Edited
SubCategory.all
instead of
Sub_category.all

Rails - using group_by and has_many :through and trying to access join table attributes

For some background, please see my question: Rails app using STI -- easiest way to pull these records?
I have some models joined with a has_many through relationship using a third join table (Recipe and Ingredient joined through RecItem).
So the guys on the prior question helped me to group my recipe's ingredients by type - this is exactly what I wanted. However, now that I am accessing #recipe.ingredients instead of #recipe.rec_items, I can't get back to the data stored in the rec_items join table (stuff like amount of ingredient, how long to cook it, etc...)
Prior to using the group_by, I was iterating through each recipe's rec_items like so:
<% #recipe.rec_items.each do |rec_item| %>
<%= rec_item.ingredient.name %><br />
<%= rec_item.amount %><br />
<% end -%>
Now that I am grouping #recipe.ingredients, how can I follow back to the rec_items related to this recipe? (If I do ingredient.rec_items it gives me rec_items for all recipes...again, I can do this with clunky statements like:
ingredient.rec_items.find_by_recipe_id(#recipe.id).amount
but that seems wrong...) Is there an easy way to accomplish these goals? (getting a list of a particular recipe's ingredients, sorted/grouped by :type, while still being able to access the additional info in rec_items for each recipe/ingredient pairing?)
Thanks!
Posting same answer again, with a little modification:
You can use group_by here. By the way you better use includes in the query for recipe, otherwise there are going to be many queries.
recipe.rec_items.group_by {|ri| ri.ingredient.type}.each do |type, rec_items|
puts type
rec_items.each do |rec_item|
puts rec_item.inspect
puts rec_item.ingredient.inspect
end
end

Resources