Check if value in one table is present in another - ruby-on-rails

So this is probably easy but I haven't been able to find the right method for it. I have 2 models. One called monitor, and one called follower.
In 'monitor' I have a column called owner_id.
In 'follower' I have a column called follower_id.
What I would like to do is check if any of these match up (I'd like to get a count, not a boolean output). Both belongs_to Users.
How do I go about doing this?
Situation
I am trying to calculate a conversion rate from Twitter. Where the follower and the follower ids are the users who is following your account.
On the monitor I let you monitor keyword and interactions, where I save the owner_id (the person you're communicating with).
Now I count all the conversations you have had. Then I want to see how many of those that have turned into following your company.
I have a model called campaigns where you can monitor certain keywords.
<% #campaigns.each do |campaign| %>
<%= campaign.keyword %>
<% end %>
The model looks like this:
class Campaign < ActiveRecord::Base
has_many :alerts
end
now what I want to do is track the conversion rate for that specific campaign.
( CODE HERE* / campaign.alerts.count ) * 100
where CODE HERE* should be the count of how many that exists between :
campaign.alerts.map(&:owner_id)
and
current_user.followers.map(&:follower_id)

so what you are trying to do is just to compare two big arrays of ids (campagin.alerts.map(&:owner_id) and current_user.followers.map(&:follower_id)) and count how many ids are the same, you can use count and count everything that your block expression evaluates as true, and save that on a variable that you will use on your division, something like this:
result = (campagin.alerts.map(&:owner_id).count do |id|
current_user.followers.map(&:follower_id)).include?(id)
end
( result / campaign.alerts.count ) * 100
There are many other ways like using Array#all or other methods, maybe you can look at them here and see what fits you most:
http://ruby-doc.org/core-2.2.0/Array.html#method-i-2D

Related

Rails sort users by method

I'm trying to rank my user's in order of an integer. The integer I'm getting is in my User Model.
def rating_number
Impression.where("impressionable_id = ?", self).count
end
This gives each User on the site a number (in integer form). Now, on the homepage, I want to show an ordered list that places these user's in order with the user with the highest number first and lowest number second. How can I accomplish this in the controller???
#users = User....???
Any help would be appreciated!
UPDATE
Using this in the controller
#users = User.all.map(&:rating_number)
and this for the view
<% #users.each do |user| %>
<li><%= user %></li>
<% end %>
shows the user's count. Unfortunately, the variable user is acting as the integer not the user, so attaching user.name doesn't work. Also, the list isn't in order based on the integer..
The advice here is still all kinds of wrong; all other answers will perform terribly. Trying to do this via a nested select count(*) is almost as bad an idea as using User.all and sorting in memory.
The correct way to do this if you want it to work on a reasonably large data set is to use counter caches and stop trying to order by the count of a related record.
Add a rating_number column to the users table, and make sure it has an index defined on it
Add a counter cache to your belongs_to:
class Impression < ActiveRecord::Base
belongs_to :user, counter_cache: :rating_number
end
Now creating/deleting impressions will modify the associated user's rating_number.
Order your results by rating_number, dead simple:
User.order(:rating_number)
The advice here is just all kinds of wrong. First of model your associations correctly. Secondly you dont ever want to do User.all and then sort it in-memory based on anything. How do you think it will perform with lots of records?
What you want to do is query your user rows and sort them based on a subquery that counts impressions for that user.
User.order("(SELECT COUNT(impressions.id) FROM impressions WHERE impressionable_id = users.id) DESC")
While this is not terribly efficient, it is still much more efficient than operating with data sets in memory. The next step is to cache the impressions count on the user itself (a la counter cache), and then use that for sorting.
It just pains me that doing User.all is the first suggestion...
If impressions is a column in your users table, you can do
User.order('impressions desc')
Edit
Since it's not a column in your users table, you can do this:
User.all.each(&:rating_number).sort {|x,y| y <=> x }
Edit
Sorry, you want this:
User.all.sort { |x, y| y.rating_number <=> x.rating_number }

Rails 4: Displaying a table of a collection of items, sorted by various item attributes?

So I have a CareerEntry model that has the following attributes: name, job_category, company, group, location, year, full_intern, and it represents the job offers that people have received. full_intern is a string that is either "internship" or "full-time", and represents what the type of the job offer is. All CareerEntries will be created by an Admin interface, so it is essentially acting as a standalone model. This is my question: given a bunch of CareerEntry objects, I want to display a table to display on my careers page (which has an action in a PagesController).
I want the table to be sorted according to multiple attributes. I want each year to be its own section in the table, then within each year, I want the internship entries grouped together and the full-time entries grouped together. Then, within these groupings, I want each job_category to be its own section (job_categories comprise of things like 'Investment Banking,' or 'Technology.')
A very good example of what I'm going for is shown under the "2013" tab in this link.
What is the best way to go about achieving this? I know that in the careers action definition of my PagesController, I could have:
class PagesController < ApplicationController
def careers
#careerentries = CareerEntry.order(:year => :desc, :fullintern => :asc, :job_category => :asc)
end
end
But this would simply return all the entries in the order that I want, and would not allow me to place headers and dividers to separate, say, the job_categories.
Is there any easier way of achieving what I'm looking for?
Perhaps you're looking for .group_by?
Group By
From the link you gave, it looks like you want to group your results by year, like this:
#careerentries = CareerEntry.order(year: :desc, fullintern: :asc, job_category: :asc)
#entries_by_year = #careerentries.group_by { |entry| entry.year }
This gives you all the data, ordered to your specs. You can then sort through it, using the group_by method:
#entries_by_year.each do |entry|
entry.name
end
You could then work this into your table
Good reference Group posts by Year - Rails

How can I sort on a calculated value?

I'm currently building an NFL pick'em league site. I have a Users model, a Games model, and join table which captures each user's individual picks. The games model has a "result" attribute which either consists of "W" for win, "L" for loss, "P" for push (tie).
I am running into issues building a standings page. I currently have two methods in my Users model:
def correct_games
self.games.where(result: "W").count
end
def total_games
self.games.where('result != ?', "P").count
end
The correct_games method counts the user's picks that were correct. The total_games methods counts the number of total games (not counting games that resulted in a push).
Then in my view I currently have for each user: <%= number_to_percentage(current_user.correct_games.to_f / current_user.total_games) %>
This division gives me that user's win percentage (# correct/total picks). For my standings table, I obivously want a descending order on win percentage. The issue is the only solutions to sorting seem to be using the .order method which usually requires some attribute to already be in the database which you can then call in the controller.
I've also tried adding this win percentage attribute to the database, but I can't seem to figure out a callback that will update the User's score whenever the game results are updated.
Any solutions to either sorthing on an attribute that is calculated in the view or a way to add this win percentage to the users model?
Thanks in advance!
Why not just do the calculation in the model instead of in the view? Add another method like this:
This code goes in your User model:
def percentage_correct
((self.correct_games.to_f / self.total_games) * 100).to_i
end
def self.sorted_by_percentage_correct
User.all.sort_by(&:percentage_correct).reverse
end
This is how you use it in your view:
<% User.sorted_by_percentage_correct.each do |u| %>
<div><%= u.name %> has pick percentage of <%= u.percentage_correct %>%</div>
<% end %>

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

How do I calculate the most popular combination of a order lines? (or any similar order/order lines db arrangement)

I'm using Ruby on Rails. I have a couple of models which fit the normal order/order lines arrangement, i.e.
class Order
has_many :order_lines
end
class OrderLines
belongs_to :order
belongs_to :product
end
class Product
has_many :order_lines
end
(greatly simplified from my real model!)
It's fairly straightforward to work out the most popular individual products via order line, but what magical ruby-fu could I use to calculate the most popular combination(s) of products ordered.
Cheers,
Graeme
My suggestion is to create an array a of Product.id numbers for each order and then do the equivalent of
h = Hash.new(0)
# for each a
h[a.sort.hash] += 1
You will naturally need to consider the scale of your operation and how much you are willing to approximate the results.
External Solution
Create a "Combination" model and index the table by the hash, then each order could increment a counter field. Another field would record exactly which combination that hash value referred to.
In-memory Solution
Look at the last 100 orders and recompute the order popularity in memory when you need it. Hash#sort will give you a sorted list of popularity hashes. You could either make a composite object that remembered what order combination was being counted, or just scan the original data looking for the hash value.
Thanks for the tip digitalross. I followed the external solution idea and did the following. It varies slightly from the suggestion as it keeps a record of individual order_combos, rather than storing a counter so it's possible to query by date as well e.g. most popular top 10 orders in the last week.
I created a method in my order which converts the list of order items to a comma separated string.
def to_s
order_lines.sort.map { |ol| ol.id }.join(",")
end
I then added a filter so the combo is created every time an order is placed.
after_save :create_order_combo
def create_order_combo
oc = OrderCombo.create(:user => user, :combo => self.to_s)
end
And finally my OrderCombo class looks something like below. I've also included a cached version of the method.
class OrderCombo
belongs_to :user
scope :by_user, lambda{ |user| where(:user_id => user.id) }
def self.top_n_orders_by_user(user,count=10)
OrderCombo.by_user(user).count(:group => :combo).sort { |a,b| a[1] <=> b[1] }.reverse[0..count-1]
end
def self.cached_top_orders_by_user(user,count=10)
Rails.cache.fetch("order_combo_#{user.id.to_s}_#{count.to_s}", :expiry => 10.minutes) { OrderCombo.top_n_orders_by_user(user, count) }
end
end
It's not perfect as it doesn't take into account increased popularity when someone orders more of one item in an order.

Resources