I need to add '%' symbol in 'votes_count' field
My controller,
#celebrity = Celebrity.includes(:category).order(votes_count: :desc)
I am counting the total votes here
total_votes = Vote.count.to_f
changing the total no. of votes to percentage
#celebrity.each do |celeb|
celeb["votes_count"] = (celeb.votes_count / total_votes * 100).round(2)
end
here, I am changing into json
respond_to do |format|
format.json { render json: #celebrity.to_json(:include =>{:category => {:only => [:category]}})}
end
My output is
[{"id":3,"name":"saravana","gender":false,"category_id":"1","votes_count":25}]
My question is how can I add '%' symbol in votes_count
You can just simply do this:
#celebrity.each do |celeb|
votes_count = ((celeb.votes_count / total_votes * 100).round(2))
celeb["votes_count"] = "#{votes_count} %"
end
Do this:
#celebrity.each do |celeb|
celeb["votes_count"] = (celeb.votes_count / total_votes * 100).round(2).to_s << "%"
end
#celebrity.each do |celeb|
celeb["votes_count"] = ((celeb.votes_count / total_votes * 100).round(2)).to_s + "%"
end
The real idea of MVC is to keep the data in its model - unformatted and present it, just how you need it, inside the view
Rails is giving you number_to_percentage
http://apidock.com/rails/ActionView/Helpers/NumberHelper/number_to_percentage
# view.html.erb
<%= number_to_percentage celeb.votes_count %>
// since i remember your last question:
you need to save the percentage at the celibrity and save into DB
if you dont you are loosing performance. imagine you have 1000 celibritys. for that you want to count every page requests 1000 times any percentages? that is stupid. if you just want to count the percentage of a current_selection of celibrities (lets say, the top3 and they are having 60/30/10 %) then your math is wrong since you relate the % to all votes and not to current_selection_votes_count)
Related
I'm sorry if this looks like a very easy (too easy) question but I'm struggling to find a way into working this out.
I need to calculate the Basal Metabolic Rate according to the data enter into a Body model and render it in my show view. The problem is that I can run the calculation in the view but, of course, this is not a proper way.
<%= ((13.397*#user.bodies.last.weight)+(479.9 * #user.bodies.last.height)-(5.677 * #user.bodies.last.age)+(88.362)) * (1 - (#user.bodies.last.fat / 100.00)) %>
this code snippet is in a partial that feeds Body#show view. Of course, I want to have this done at the controller level.
So I wrote a method in my bodies_controller that looks like this:
# Calculate Basal Metabolic Rate for Males
def bmr
#user = User.find(params[:user_id])
#bmr = ((13.397 * #user.bodies.last.weight) + (479.9 *
#user.bodies.last.height) - (5.677 * #user.bodies.last.age) +
88.362) * (1 - (#user.bodies.last.fat / 100.00))
end
When trying to pull the result of this simple calculation into my Body#show view like this: <%= #bmr %> nothing shows. Furthermore typing #bmr into the console returns NIL. How do I feed the Body attributes (Weight, age, height....) into the method?
Sorry if this question sounds stupid but I'm still learning.
Thanks a million in advance!
you can move bmr method to body model as follow
body.rb
class Body < ApplicationRecord
# your other codes
def bmr
((13.397 * self.weight) +
(479.9 * self.height) -
(5.677 * self.age) + 88.362) *
(1 - (self.fat / 100.00))
end
end
for your note: self is equal to last bodies object
and from your view you can call the result as follow
show.html.erb
<%= #user.bodies.last.bmr %>
There are a few pointers I'd give you in solving this more elegantly. Breakdown the problem into more manageable parts, and try to stay away from magic numbers.
class Body
def bmr
#bmr ||= Bmr.calculate(self)
end
end
(below) obviously, replace num1 etc. with descriptive names for those numbers so we have an idea of what they mean in this calculation. Also, consider using constants NUM1 defined at top of class instead of methods.
class Bmr
attr_reader :body
private :body
def initialize(body)
#body = body
end
def self.calculate(body)
new(body).calculate
end
def calculate
((num1 * body.weight) +
(num2 * body.height) -
(num3 * body.age) +
(num4) *
(num5 - (body.fat / 100.00))
end
def num1
13.397
end
def num2
479.9
end
def num3
5.677
end
def num4
88.362
end
def num5
1
end
end
On top of that, you could further break the inner calculations down..
def calculated_weight
num1 * body.weight
end
# then
def calculate
calculated_weight + calculated_height - calculated_age
# etc
end
This gives you a great opportunity to describe the overall calculation and makes it much easier to grasp at a glance, and dig into if necessary.
Right now I have this
def index
#trips = Trip.all
end
And I'm outputting data like so:
- #trips.order('created_at desc').first(4).each do |trip|
- trip.trip_images.first(1).each do |image|
= trip.title_name.titleize
However, I have a votable table (from acts_as_votable gem) associated to trips. I was wondering if I can only output trips where trips have a certain amount of votes?
I can get the votes like this:
- #trips.order('created_at desc').first(4).each do |trip|
= trip.get_likes.size #this is where I can get the likes
- trip.trip_images.first(1).each do |image|
= trip.title_name.titleize
EDIT
If I do this instead:
def index
#votes = ActsAsVotable::Vote.where(votable_type: 'Trip').group(:votable_id).count
#trips = Trip.where(#votes)
end
#votes gives me something like this:
{195=>1, 106=>1, 120=>1, 227=>1, 247=>1, 264=>1, 410=>1}
How do I get it where trip will only get the ids?
EDIT 2
I think I figured it out...
def index
#votes = ActsAsVotable::Vote.where(votable_type: 'Trip').group(:votable_id).count
#trips = Trip.where(id: #votes.keys)
end
I got some kind of output. Is there a better way?
Yesterday I answered similar question.
This is how you could get the id(s) of trip with certain amount of votes (you can use =, >, <= and so on):
trip_ids = ActsAsVotable::Vote
.where(votable_type: 'Trip')
.group(:votable_id)
.having('count(votable_id) > 1') #any number of votes
.pluck(:votable_id)
.uniq
Trip.where(id: trip_ids)
Have you considered making this a method in your Trip model?
Something like,
def popular_trip_images
Trip.select(:trip_images).where("likes > ?", 200)
end
Then use it something like,
...
trip.popular_trip_images.each do |image|
...
Edit:
However, I have a votable table (from acts_as_votable gem) associated to trips. I was wondering if I can only output trips where trips have a certain amount of votes?
Sorry, missed this part. The gem has a find_liked_items method but don't see offhand how to set something like liked > 400 etc.
I've been trying to work through the comments, but right now, I've gotten this code to work:
def index
#votes = ActsAsVotable::Vote.where(votable_type: 'Trip').group(:votable_id).count
#votes = #votes.select {|k,v| v > 1}
#trips = Trip.where(id: #votes.keys)
end
If someone else can come up with a better solution! I'll mark as correct.
I have this "heavy_rotation" filter I'm working on. Basically it grabs tracks from our database based on certain parameters (a mixture of listens_count, staff_pick, purchase_count, to name a few)
An xhr request is made to the filter_tracks controller action. In there I have a flag to check if it's "heavy_rotation". I will likely move this to the model (cos this controller is getting fat)... Anyway, how can I ensure (in a efficient way) to not have it pull the same records? I've considered an offset, but than I have to keep track of the offset for every query. Or maybe store track.id's to compare against for each query? Any ideas? I'm having trouble thinking of an elegant way to do this.
Maybe it should be noted that a limit of 14 is set via Javascript, and when a user hits "view more" to paginate, it sends another request to filter_tracks.
Any help appreciated! Thanks!
def filter_tracks
params[:limit] ||= 50
params[:offset] ||= 0
params[:order] ||= 'heavy_rotation'
# heavy rotation filter flag
heavy_rotation ||= (params[:order] == 'heavy_rotation')
#result_offset = params[:offset]
#tracks = Track.ready.with_artist
params[:order] = "tracks.#{params[:order]}" unless heavy_rotation
if params[:order]
order = params[:order]
order.match(/artist.*/){|m|
params[:order] = params[:order].sub /tracks\./, ''
}
order.match(/title.*/){|m|
params[:order] = params[:order].sub /tracks.(title)(.*)/i, 'LOWER(\1)\2'
}
end
searched = params[:q] && params[:q][:search].present?
#tracks = parse_params(params[:q], #tracks)
#tracks = #tracks.offset(params[:offset])
#result_count = #tracks.count
#tracks = #tracks.order(params[:order], 'tracks.updated_at DESC').limit(params[:limit]) unless heavy_rotation
# structure heavy rotation results
if heavy_rotation
puts "*" * 300
week_ago = Time.now - 7.days
two_weeks_ago = Time.now - 14.days
three_months_ago = Time.now - 3.months
# mix in top licensed tracks within last 3 months
t = Track.top_licensed
tracks_top_licensed = t.where(
"tracks.updated_at >= :top",
top: three_months_ago).limit(5)
# mix top listened to tracks within last two weeks
tracks_top_listens = #tracks.order('tracks.listens_count DESC').where(
"tracks.updated_at >= :top",
top: two_weeks_ago)
.limit(3)
# mix top downloaded tracks within last two weeks
tracks_top_downloaded = #tracks.order("tracks.downloads_count DESC").where(
"tracks.updated_at >= :top",
top: two_weeks_ago)
.limit(2)
# mix in 25% of staff picks added within 3 months
tracks_staff_picks = Track.ready.staff_picks.
includes(:artist).order("tracks.created_at DESC").where(
"tracks.updated_at >= :top",
top: three_months_ago)
.limit(4)
#tracks = tracks_top_licensed + tracks_top_listens + tracks_top_downloaded + tracks_staff_picks
end
render partial: "shared/results"
end
I think seeking an "elegant" solution is going to yield many diverse opinions, so I'll offer one approach and my reasoning. In my design decision, I feel that in this case it's optimal and elegant to enforce uniqueness on query intersections by filtering the returned record objects instead of trying to restrict the query to only yield unique results. As for getting contiguous results for pagination, on the other hand, I would store offsets from each query and use it as the starting point for the next query using instance variables or sessions, depending on how the data needs to be persisted.
Here's a gist to my refactored version of your code with a solution implemented and comments explaining why I chose to use certain logic or data structures: https://gist.github.com/femmestem/2b539abe92e9813c02da
#filter_tracks holds a hash map #tracks_offset which the other methods can access and update; each of the query methods holds the responsibility of adding its own offset key to #tracks_offset.
#filter_tracks also holds a collection of track id's for tracks that already appear in the results.
If you need persistence, make #tracks_offset and #track_ids sessions/cookies instead of instance variables. The logic should be the same. If you use sessions to store the offsets and id's from results, remember to clear them when your user is done interacting with this feature.
See below. Note, I refactored your #filter_tracks method to separate the responsibilities into 9 different methods: #filter_tracks, #heavy_rotation, #order_by_params, #heavy_rotation?, #validate_and_return_top_results, and #tracks_top_licensed... #tracks_top_<whatever>. This will make my notes easier to follow and your code more maintainable.
def filter_tracks
# Does this need to be so high when JavaScript limits display to 14?
#limit ||= 50
#tracks_offset ||= {}
#tracks_offset[:default] ||= 0
#result_track_ids ||= []
#order ||= params[:order] || 'heavy_rotation'
tracks = Track.ready.with_artist
tracks = parse_params(params[:q], tracks)
#result_count = tracks.count
# Checks for heavy_rotation filter flag
if heavy_rotation? #order
#tracks = heavy_rotation
else
#tracks = order_by_params
end
render partial: "shared/results"
end
All #heavy_rotation does is call the various query methods. This makes it easy to add, modify, or delete any one of the query methods as criteria changes without affecting any other method.
def heavy_rotation
week_ago = Time.now - 7.days
two_weeks_ago = Time.now - 14.days
three_months_ago = Time.now - 3.months
tracks_top_licensed(date_range: three_months_ago, max_results: 5) +
tracks_top_listens(date_range: two_weeks_ago, max_results: 3) +
tracks_top_downloaded(date_range: two_weeks_ago, max_results: 2) +
tracks_staff_picks(date_range: three_months_ago, max_results: 4)
end
Here's what one of the query methods looks like. They're all basically the same, but with custom SQL/ORM queries. You'll notice that I'm not setting the :limit parameter to the number of results that I want the query method to return. This would create a problem if one of the records returned is duplicated by another query method, like if the same track was returned by staff_picks and top_downloaded. Then I would have to make an additional query to get another record. That's not a wrong decision, just one I didn't decide to do.
def tracks_top_licensed(args = {})
args = #default.merge args
max = args[:max_results]
date_range = args[:date_range]
# Adds own offset key to #filter_tracks hash map => #tracks_offset
#tracks_offset[:top_licensed] ||= 0
unfiltered_results = Track.top_licensed
.where("tracks.updated_at >= :date_range", date_range: date_range)
.limit(#limit)
.offset(#tracks_offset[:top_licensed])
top_tracks = validate_and_return_top_results(unfiltered_results, max)
# Add offset of your most recent query to the cumulative offset
# so triggering 'view more'/pagination returns contiguous results
#tracks_offset[:top_licensed] += top_tracks[:offset]
top_tracks[:top_results]
end
In each query method, I'm cleaning the record objects through a custom method #validate_and_return_top_results. My validator checks through the record objects for duplicates against the #track_ids collection in its ancestor method #filter_tracks. It then returns the number of records specified by its caller.
def validate_and_return_top_results(collection, max = 1)
top_results = []
i = 0 # offset incrementer
until top_results.count >= max do
# Checks if track has already appeared in the results
unless #result_track_ids.include? collection[i].id
# this will be returned to the caller
top_results << collection[i]
# this is the point of reference to validate your query method results
#result_track_ids << collection[i].id
end
i += 1
end
{ top_results: top_results, offset: i }
end
I have a model named Vendor. I have three different models associated with it.
Testimonial
Service
Works
I have to look up each of the tables and list the vendors(limit 5) which have the word "example" most number of times in a column in one of these models. How do I do this in rails?
I have something like this
Vendor.joins(:testimonials).where("testimonials.content LIKE ?","%example%")
I need to find the vendors who has the maximum number of occurrences of the word "example"
I hope I got you right now:
a=[]
vendors.each do |v|
c=0
c=v.testimonial.scan(/example/).count
if c>0
a=a.concat([{vendor: v, counts: c}])
end
In Ruby you can count the occurrence of a substring in a string in this way, for example
s = "this is a string with an example, example, example"
s.scan(/example/).count # => 3
This is how I ended up doing this. Not sure if my question was asked correctly. I made this with the help of a previous answer by #user3383458
vendor = Hash.new
Vendor.all.each do |v|
testi_word_count = 0
service_word_count = 0
title_word_count = 0
v.testimonials.each do |testi|
testi_word_count += testi.content.scan(/#{word_to_search}/).count
Rails.logger.info "testi_word_count"
Rails.logger.info testi_word_count
end
v.services.each do |service|
service_word_count += service.name.scan(/#{word_to_search}/).count
Rails.logger.info "service_word_count"
Rails.logger.info service_word_count
end
v.works.each do |work|
title_word_count += work.title.scan(/#{word_to_search}/).count
Rails.logger.info "title_word_count"
Rails.logger.info title_word_count
end
vendor[v]=testi_word_count + service_word_count + title_word_count
end
Im trying to get the average of price in the products table and store it in a variable called average.
Heres my code:
def index
#products = Product.all
#average = 0
#i = 0
#products.each do |p|
i += 1
average += p.price
end
average = average / i
end
Im getting error "undefined method `+' for nil:NilClass" on the "i += 1 statement"
In Ruby on Rails there are predefined aggregate methods for your models to calculate average, sum, count, minimum, maximum, etc.
In your special case, you can do the following:
#average = Product.average(:price)
It will dispatch the actual work to the SQL which will do a better job than a Ruby code. It will produce an SQL query like (this is a MySQL example):
SELECT AVG(`products`.`price`) AS avg_id FROM `products`
You're setting up i as an instance variable (#i), but calling it as a regular variable (i).
Either take away the # when you initialize it to zero, or change references to i to #i.
You probably don't want to re-use your i after this method, so you probably just want a regular variable. Like so. (You also have a similar problem with your #average, which you probably do want to persist after the method.)
def index
#products = Product.all
#average = 0
i = 0
#products.each do |p|
i += 1
#average += p.price
end
#average = #average / i
end
There is a much cleaner way of working out averages from ActiveRecord collections (you could be doing it directly in the SQL), but I won't mention those since what you're doing is probably a good way to learn Ruby.
Use.. i think you forgot # sign
def index
#products = Product.all
#average = 0
#i = 0
#products.each do |p|
#i += 1
#average += p.price
end
#average = #average / #i
end