Get distinct values of an has_many field - ruby

I have a model :
class Hotel < ActiveRecord::Base
has_many :hotel_comments
end
In my controller, i get some hotels:
#hotels = Hotel.all
How can I get all comments for my hotel dictionary?
Thank you!

Guessing what you mean by "dictionary":
Hash[Hotel.includes(:hotel_comments).map { |h| [h, h.hotel_comments] }]
If you just want all comments in a array:
Hotel.includes(:hotel_comments).map(&:hotel_comments).flatten(1)

Without seeing more of your model, I am also guessing a bit here too, but how about:
#biglist=new(array)
#hotels.for_each do |h|
sub_list=HotelComments.find_by_hotel_id(h.id)
#big_list.push(sub_list)
end
Or, if HotelComments is large, something like:
#biglist=new(array)
ids=new(array)
#hotels.for_each do |h|
ids.push(h.id)
end
#big_list=HotelComments.find_by_hotel_id(ids)
... would gather up the comments with one pass through HotelComments
You may find (much) better thoughts here: http://guides.rubyonrails.org/active_record_querying.html

Related

Ordering of relation based on associated table in rails4

I have 2 models as below:
class Student < ActiveRecord::Base
has_many :marks
end
class Mark < ActiveRecord::Base
belongs_to :student
end
point is a field in marks table. For each student there are many entries in the marks table.
I need to get a list of students based on the order of highest total points.
I tried as below:
#students = Student.all.collect{|p| [p,p.marks.pluck(:point).sum]}
#students.sort_by { |h| h[1] }.reverse!
But it will return 2 items in each array, one the object and next the total points.
Is there a better solution please.
Thanks,
Jissy
This should do the trick:
Student.joins(:marks).select('students.*, SUM(marks.point) AS total_point').group(:id).order('total_point DESC')
You can do it like this.
Student.joins(:marks).select('id, sum(marks.point) as total_marks').group('students.id').order('total_marks desc')
If you are still unable to run it please modify it or let me know.In place of id in select you can select any column(s).

How to get an array of values from a conditional ActiveRecord query with associations?

I'm looking to define a method on one of my objects that will return a just one column of data from all of its child objects so long as another column in the same record meets certain conditions.
For instance if I have two objects
ParentObject
has_many: child_objects
#fields
name (string)
ChildObject
belongs_to: parent_object
#fields
name (string)
whitelisted_at (datetime)
I've read up that I can get a list of all child_object records for a parent_object based on a conditional specified using .where(). For instance in my controller I have code like so:
ParentObject.child_objects.where("whitelisted_at IS NOT NULL")
This gives me an active record associate like so:
#<ActiveRecord::AssociationRelation [
<ChildObject id: 1, name:"Susan", whitelisted_at: "2015-02-18 12:07:37">,
<ChildObject id: 1, name:"Simon", whitelisted_at: "2015-02-18 12:07:37">,
<ChildObject id: 1, name:"Sally", whitelisted_at: "2015-02-18 12:07:37">
]
I was looking how I would then filter through these to return an array of just names. Ideally i'd be able to run this all as a Model method so:
class ChainObject < ActiveRecord::Base
...
def whitelisted_names
#... outputs [Susan, Simon, Sally]
end
end
What would be the most concise and rails-y way of doing this. I thought about doing a .where() then an .each() and having a block method but that seems really cumbersome and I'm sure I'm just missing some smart ActiveRecord or Association method that could pluck an array of values from multiple hashes. I'm pouring over the APIdock but I think the problem is I don't know how to describe what I'm trying to do!
In your parent model you could use where.not and use the pluck method ActiveRecord gives you (props to Stefan - see pluck)
class ParentObject < ActiveRecord::Base
...
def whitelisted_names
child_objects.where.not(whitelisted_at: nil).pluck(:name)
end
end

returning array without specific elements in ruby

I looked really hard in http://www.ruby-doc.org/core-2.1.2/Array.html but I couldn't find a quick functionality to this behaviour:
arr = [1,2,3,4,5,6]
arr.without(3,6) #=> [1,2,4,5]
I know I can write my own function/monkey-patch ruby/add a class method/write it in a few lines.
Is there a way to do this in a ruby way?
you can use subtraction :
arr - [3,6]
EDIT
if you really wanted you could alias this method
class Array
alias except -
end
then you can use:
arr.except [3,6]
This got added in Rails 5 :)
https://github.com/rails/rails/issues/19082
module Enumerable
def without(*elements)
reject { |element| element.in?(elements) }
end
end
it's just aesthetics, but it makes sense for the brain
There is another way using reject. But it is not cleaner than -
arr.reject{|x| [3,6].include? x}
Just in case anyone else comes across this and is looking for a way to delete elements from an array based on a conditional: you can use delete_if.
For example, I have a list of customers and want to merge any customers that have duplicate emails. After doing a query to get a list of all emails with the total count number for each email, I can then delete all of them that only appear once:
emails = Customer.select('count(email) as num_emails, email').group('email')
emails.delete_if { |email| email.num_emails.to_i == 1 }
The end result is I have a list of all customer emails that appear multiple times in the database.

Why can't I make a new hash from selected keys in ruby?

This has been bugging me for a while. It's not a difficult thing, but I don't know why there's no easy way to do it already, and I bet there is and I don't see it.
I just want to take a hash, like this:
cars = {:bob => 'Pontiac', :fred => 'Chrysler',
:lisa => 'Cadillac', :mary => 'Jaguar'}
and do something like
cars[:bob, :lisa]
and get
{:bob => 'Pontiac', :lisa => 'Cadillac'}
I did this, which works fine:
class Hash
def pick(*keys)
Hash[select { |k, v| keys.include?(k) }]
end
end
ruby-1.8.7-p249 :008 > cars.pick(:bob, :lisa)
=> {:bob=>"Pontiac", :lisa=>"Cadillac"}
There's obviously a zillion easy ways to do this, but I'm wondering if there's something built in I've missed, or a good an un-obvious reason it's not a standard and normal thing? Without it, I wind up using something like:
chosen_cars = {:bob => cars[:bob], :lisa => cars[:lisa]}
which isn't the end of the world, but it's not very pretty. It seems like this should be part of the regular vocabulary. What am I missing here?
(related questions, include this: Ruby Hash Whitelist Filter)
(this blog post has precisely the same result as me, but again, why isn't this built in? http://matthewbass.com/2008/06/26/picking-values-from-ruby-hashes/ )
update:
I'm using Rails, which has ActiveSupport::CoreExtensions::Hash::Slice, which works exactly as I want it to, so problem solved, but still... maybe someone else will find their answer here :)
Just to help others, some years after the fact:
Slice works nicely to do what I wanted.
> cars.slice(:bob, :lisa)
=> {:bob=>"Pontiac", :lisa=>"Cadillac"}
I've always thought that was a weird omission as well, but there really is no simple, standard method for that.
Your example above might be unnecessarily slow because it iterates over all the hash entries whether we need them or not, and then repeatedly searches through the keys parameter array. This code should be a bit faster (assuming that would ever matter -- and I haven't tried to benchmark it).
class Hash
def pick(*keys)
values = values_at(*keys)
Hash[keys.zip(values)]
end
end
select deserves at least to be mentioned:
cars = {:bob => 'Pontiac', :fred => 'Chrysler',
:lisa => 'Cadillac', :mary => 'Jaguar'}
people = [:bob, :lisa]
p cars.select{|k, _| people.include?(k)}
#=> {:bob=>"Pontiac", :lisa=>"Cadillac"}
Ruby makes it possible to add that feature without much pain:
class Hash
alias old_accessor :[]
def [](*key)
key.is_a?(Array) ? self.dup.delete_if{|k, v| !key.include? k} : old_accessor(key)
end
end
I hope this helps. I know it's not a built-in feature.
{ height: '178cm', weight: '181lbs', salary: '$2/hour' }.select { |k,v| [:height, :weight].include?(k) }

Populate an new array from another array of symbols

I have a question about Ruby arrays but it's kind of hard to describe so I could not find much from reading other questions. Here it is.
Currently I have the following code that works (part of Prawn's table)
Snippet A:
students = all_students.map do |student|
[
student[:first_name],
student[:last_name],
student[:email],
student[:given_name]
]
end
pdf.table students
This works fine, but now I'd like to omit some of the columns (e.g. don't show last_name).
Say I get an array of column names, let's say pickedColumns:
Snippet B:
pickedColumns = []
pickedColumns << :first_name << :email << :given_name #NOTE: no (:last_name) there!
students = all_students.map do |student|
studentCols = pickedColumns.each do |studentCol|
student[studentCol]
end
end
p.table students
I haven't been able to achieve the effect of snippet A, using the replaced code in snippet B. All I get in snippet B are not the actual values of "student[:first_name]" of just a string "first_name" for each row.
If my description is not 100% clear, please let me know.
Thanks for you help!
Regards
students = all_students.map do |student|
studentCols = pickedColumns.each do |studentCol|
student[studentCol]
end
end
Make that
students = all_students.map do |student|
pickedColumns.map do |studentCol|
student[studentCol]
end
end
and it will work.
PS: To adhere to ruby's naming convention you should change your variable names to use all lower case and underscores, rather than camelCase.

Resources