Ruby Compare Attributes in Array of Objects - ruby-on-rails

I have an User model and a Concert model in my Ruby on Rails project. The User has an array containing all the concerts of his favorite bands, and the Concert model has the city and date attributes.
What I need to do is iterate through this array of concerts to see if they´re any concerts that are happening the same day in the same city.
Something like this:
#user_concerts = [concert1, concert2, concert3....]
I guess I'll have to loop over this array two times, but I'm a bit stuck with this.
The way my DB is made, I use the query below to get the IDs of the bands favorited by a user.
bands_ids_array = UsersBand.where(user_id: current_user.id, is_favorite: true).pluck(:band_id)
#user_favorite_bands = []
bands_ids_array.each do |id|
#user_favorite_bands << Band.find(id)
end
UsersBand is the relational DB between User and Band models. It contains user_id, band_id and a Boolean field is_favorite.
To get all the concerts I do the following currently:
#user_concerts = []
#user_favorite_bands.each do |band|
band.concerts.each do |concert|
#user_concerts.push(concert)
end
end
I´ve tried nesting two "each" loops, a while loop nested inside an "each" trying to compare all the concerts to see if they have the same city or date, but that didn´t work.
Edit: I did that and it worked for me:
for i in (0..#user_concerts.length) do
for j in (i+1..#user_concerts.length-1) do
if #user_concerts[i].city == #user_concerts[j].city && #user_concerts[i].date == #user_concerts[j].date
matches.push(#user_concerts[i])
matches.push(#user_concerts[j])
end
end
Any help will be appreciated. Thanks a lot guys.

Related

Best way to query without a join table ActiveRecord

So I have a table trips and a table conditions. Trips has a start_date and end_date, and neither last more than a few hours on the same day. Conditions has a date column. I am trying to compare them to find the trip.id of a date in order to seed with a :trip_id foreign key located in the conditions table with a very large csv. I understand that it is a many_to_many relationship, but I am wondering how to convert what I wrote in Ruby (transitioning to rails currently) to the "ActiveRecord" way without hitting the database so many times.
Here is my code:
Helper method
def self.id_by_date(date)
find_by(date: date).id
end
Main method
def self.sort_temp(range)
array = where(max_temperature: range).all.map {|condition| condition.date}
trip_nums = array.map do |date|
Trip.where(start_date: date.beginning_of_day...date.end_of_day).count
end
output = {}
output[:max] = trip_nums.sort.last
output[:min] = trip_nums.sort.reverse.last
output[:avg] = trip_nums.inject(:+) / trip_nums.length unless trip_nums.length == 0
output
end
EDIT: To be clear, I am trying to find all conditions associated with a day

How to rewrite this each loop in ruby?

I have this loop:
stations = Station.where(...)
stations.all.each do |s|
if s.city_id == city.id
show_stations << s
end
end
This works well, but because of looping the all the data, I think it's kinda slow. I've tried to rewrite it with using select, like this:
show_stations << stations.select { |station| station.city_id == city.id}
But the amount of saved data into show_stations is different compared to the each version and then, the data are in different format (array/object).
Is there any better/faster way to rewrite the loop version?
The fastest version of this maybe the built-in rails ActiveRecord method for finding associated objects.
So provided your Station model contains this:
class Station < ActiveRecord::Base
belongs_to :city
And your City model contains this:
class City < ActiveRecord::Base
has_many :stations
Then rails automatically generates the method city.stations which automatically fetches the stations which contain that city's id from the database. It should be pretty optimized.
If you want to make it even faster then you can add add_index :stations, :city_id to your table in a migration and it will retrieve faster. Note that this only saves time when you have a lot of stations to search through.
If you need to make it an array you can just convert it after with city.stations.to_a. And if you wanted to narrow it further, just use the select method and add the conditions that you wanted to previously add in your Station.where(...) statement.
(e.g. city.stations.to_a.select { |item| your_filter })
You should also cache the query results like
stations ||= Station.where("your where").where(:city_id => city.id)
Maybe you need to include into the where clause the city parameter:
stations = Station.where("your where").where(:city_id => city.id)
or the same
stations = Station.where("your where").where('city_id = ?', city.id)
Station appears to be an active record model. If that is the case, and you don't need all the stations, you can add the city.id filter to your where statement.
The issue you're having now is that you're adding the array returned from select as the last item of show_stations. If you want show_stations to only contain stations that match city.id then use show_stations = ... rather than show_stations << .... If you want show_stations to contain what it already contains plus the stations that match city.id then use show_stations + stations.select { |station| station.city_id == city.id }. (There are a number of other approaches for adding two arrays together.)

Comparing two lists and syncronizing the database based on differences

I have the following resource relationship:
Class Course < ActiveRecord::Base
has_many :track_courses
has_many :tracks, through: :track_courses
end
as well as a mirroring relationship inside the Track model. The TrackCourse table which connects these models has these rows:
id: primary key
track_id: represents the track
course_id: represents the course
position: the ordering of the course inside that track
I want to allow admin users to be able to update the courses in each track via ajax. I have a list on the front-end that is being passed to the controller as a hash:
front_end_list = { course_id => position }
which represents the object and its position on the front-end sortable.
I'm also looking up the list of existing courses in that track:
existing_courses = TrackCourse.where("track_id = ?", track_id).all
GOAL: Compare these two lists and syncronize the database entries according to the front-end list. Essentially, if the user inserts Course 15 into position 2 on the webpage, I need to either insert that entry into TrackCourse table (if it doesn't exist) or update its position (if it exists). And vice versa for remove.
What is the best way of doing this? Do ActiveRecord/ActiveRelation provide methods for it? Or do I have to write something myself?
UPDATE: I found a gem called acts_as_list, but it seems to be designed for ActiveRecord tables as opposed to ActiveRelation. It essentially expects position values to be unique, whereas in TrackCourse there can be multiple course with same position (in different tracks).
I figured out a solution. I'll post my code here in case it helps anyone else down the line.
I have this method in my controller that processes the ajax request from the front-end:
def sort
track_id = params[:track_id]
courses_in_list = {}
params[:course].each do |courseid|
position = params[:course].index(courseid)
courses_in_list[courseid.to_i] = position
end
existing_courses_in_track = {}
TrackCourse.where("track_id = ?", track_id).to_a.each do |track_course|
existing_courses_in_track[track_course.course_id] = track_course.position
end
if courses_in_list.length < existing_courses_in_track.length
existing_courses_in_track.each do |courseid, position|
if courses_in_list[courseid].nil?
track_course = TrackCourse.where(track_id: track_id, course_id: courseid).first
track_course.remove_from_list
track_course.destroy!
end
end
else
if existing_courses_in_track.empty?
track_course = TrackCourse.new(track_id: track_id,
course_id: courses_in_list.keys[0])
track_course.insert_at(courses_in_list.values[0])
p "first track!"
else
courses_in_list.each do |courseid, position|
track_exists = false
if !existing_courses_in_track[courseid].nil?
track_course_position = existing_courses_in_track[courseid]
track_exists = true
end
if !track_exists
TrackCourse.new(track_id: track_id, course_id: courseid).insert_at(position)
else
p "else statement"
track_course = TrackCourse.where(track_id: track_id, course_id: courseid).first
track_course.update_attribute(:position, position)
end
end
end
end
render :nothing => true
end
Essentially, I'm building two hashes, one based on the list of front-end items and their position, and one based on the database courses and their position. I then compare them. If the front-end list is shorter, that means the user removed an item, so I iterate through the backend list, find the extra item, and remove it. Then I employ a similar mechanism for adding items to the list and resorting the list. The acts_as_list gem really helps with keeping things in the correct position. However, I did have to limit its scope when I included it in my model to ensure it runs only on relationships (TrackCourses) with a specific track_id.

Ruby on Rails. Searching ActiveRecord objects where condition is the presence of one of the associations

I have a Rails code that queries database for employee objects, which have many social_url_for_service (for various services). The way it is implemented, it first gets all employees from database, and then serches for ones with Twitter. Is there any way to look for this association directly (with Employee.find() metod for example) ?
#e = Employee.all
#employees = []
#tweets = []
#e.each do |employee|
if employee.social_url_for_service(:twitter)
#employees << employee
#tweets.concat(Twitter.user_timeline(employee.social_url_for_service(:twitter).split('/').last, count: 3))
end
end
Assuming social_url_for_service is a method that grabs a social_service_link association with a service_name field:
Employee.joins(:social_service_links).where('social_service_links.service_name = ?', "Twitter")
You'll need to update this for your exact table and field names. You can also drop the .where call to return all employees with a service of any kind.

How to Average Multiple Columns in Rails

I have the following objects: Products, Ratings, and Users. I am trying to filter and select a number of Products that the User owns (through a has_many :through relationship with UserProducts) and average a certain column the Ratings table that matches their User ID and the correct Product ID.
So, my function is something along these lines:
def find_rating(criteria)
product = self.products.find(:all, :conditions => ["criteria = ?", criteria])
rating = self.ratings.where("product_id = ?", product).average(:overall)
end
I think that I'm going about this the wrong way, because I'm trying to find a product_id by passing an entire array of data consisting of multiple products. But, I think of using a more traditional loop and that seems convoluted. Can someone point me in the right direction for solving this problem? Thanks!
If product is a single entry, as it appears to be in your code, I would do this:
rating = self.products.find_by_criteria(criteria).ratings.average(:overall)
If it's an array of products, this method may help you: http://apidock.com/rails/ActiveRecord/Batches/ClassMethods/find_each

Resources