How to get a variable to have 2 decimals - ruby-on-rails

I have a variable i would like to force to have 2 and always 2 decimals. Im comparing to a currency. Often i get a comparison looking like the following.
if self.price != price
#do something
end
Where self.price = 120.00 and price = 120.0. The self.price is set with a :precision => 2 in the model, but how do i do the same with a variable, cause this seems to fail in comparison

Use integers for storing currency, for example, use store 100 cents for 1 dollar. It reduces headaches and may improve performance if it matters.

class Numeric
def round_to( decimals=0 )
factor = 10.0**decimals
(self*factor).round / factor
end
end
if self.price.round_to(2) != price.round_to(2)
#do something
end

Related

Ruby/Rails - Converting an integer into a float excluding existing zeros

There must be a simple way to achieve this, I have an DB field containing an integer and I want to reformat it into a float to display.
As an integer my value looks like 6500 and I want it to display as 65.00
Within my model I have attempted to achieve this by creating the following method
def get_payment_amount_as_number
amount = self.payment_amount
return '%.02f' % self.payment_amount.to_f
end
Which results in the following being displayed: 6500.00
What would the best approach be to either strip the initial zeroes or to simply insert a decimal point?
Whilst I imagine this a ruby related question, I am not sure if rails has a handy helper already in place?
Thank you.
You could divide the number by 100:
payment_amount = 6595
'%.02f' % payment_amount.fdiv(100)
#=> "65.95"
'%.02f' % (payment_amount / 100.0)
#=> "65.95"
Or you could convert the number to a string and insert a decimal point:
payment_amount.to_s.rjust(3, '0').insert(-3, '.')
#=> "65.95"
Rails also provides several helpers to format numbers:
number_to_currency(65.95)
#=> "$65.95"
number_to_currency(1000)
#=> "$1,000.00"
And you might want to take a look at the money-rails gem which provides a mapping from cents to money objects.
You do this simply ...
def get_payment_amount_as_number
amount = self.payment_amount / 100
#to convert amount to float
amount.to_f
end
I find another one
amount = self.payment_amount
# => 6500
ans = '%.2f' % (amount/100)
# => "65.00"
int_value = 6500
float_value = float_value = '%.2f' % (int_value / 100.0)
puts int_value: int_value, float_value: float_value
it's all!

Generate array of daily avg values from db table (Rails)

Context:
Trying to generating an array with 1 element for each created_at day in db table. Each element is the average of the points (integer) column from records with that created_at day.
This will later be graphed to display the avg number of points on each day.
Result:
I've been successful in doing this, but it feels like an unnecessary amount of code to generate the desired result.
Code:
def daily_avg
# get all data for current user
records = current_user.rounds
# make array of long dates
long_date_array = records.pluck(:created_at)
# create array to store short dates
short_date_array = []
# remove time of day
long_date_array.each do |date|
short_date_array << date.strftime('%Y%m%d')
end
# remove duplicate dates
short_date_array.uniq!
# array of avg by date
array_of_avg_values = []
# iterate through each day
short_date_array.each do |date|
temp_array = []
# make array of records with this day
records.each do |record|
if date === record.created_at.strftime('%Y%m%d')
temp_array << record.audio_points
end
end
# calc avg by day and append to array_of_avg_values
array_of_avg_values << temp_array.inject(0.0) { |sum, el| sum + el } / temp_array.size
end
render json: array_of_avg_values
end
Question:
I think this is a common extraction problem needing to be solved by lots of applications, so I'm wondering if there's a known repeatable pattern for solving something like this?
Or a more optimal way to solve this?
(I'm barely a junior developer so any advice you can share would be appreciated!)
Yes, that's a lot of unnecessary stuff when you can just go down to SQL to do it (I'm assuming you have a class called Round in your app):
class Round
DAILY_AVERAGE_SELECT = "SELECT
DATE(rounds.created_at) AS day_date,
AVG(rounds.audio_points) AS audio_points
FROM rounds
WHERE rounds.user_id = ?
GROUP BY DATE(rounds.created_at)
"
def self.daily_average(user_id)
connection.select_all(sanitize_sql_array([DAILY_AVERAGE_SELECT, user_id]), "daily-average")
end
end
Doing this straight into the database will be faster (and also include less code) than doing it in ruby as you're doing now.
I advice you to do something like this:
grouped =
records.order(:created_at).group_by do |r|
r.created_at.strftime('%Y%m%d')
end
At first here you generate proper SQL near to that you wish to get in first approximation, then group result records by created_at field converted to just a date.
points =
grouped.map do |(date, values)|
[ date, values.reduce(0.0, :audio_points) / values.size ]
end.to_h
# => { "1-1-1970" => 155.0, ... }
Then you remap your grouped hash via array, to calculate average values with audio_points.
You can use group and calculations methods built in AR: http://guides.rubyonrails.org/active_record_querying.html#group
http://guides.rubyonrails.org/active_record_querying.html#calculations

How do I control the decimals in my float calculations?

I'm trying to run a few calculations in order to represent a particular price (ie 20.30).
I have tried the Float#round method, but the instance variables holding these values eventually start representing numbers that look like 24.43418 after a few calculations.
This is just a method I created to turn a users input into a percentage
class Fixnum
def percentage
self.to_f / 100
end
end
The #bankroll_amount and #risk_amount values should be evaluating to two decimal points
class Client
def initialize(bankroll, unit)
#bankroll_amount = bankroll.to_i.round(2)
#unit_percentage = unit.to_i.percentage
default_risk_amount.round(2)
evaluate_default_unit!.round(2)
end
def default_risk_amount
#risk_amount = #unit_percentage * #bankroll_amount
#risk_amount.round(2)
end
# simulates what an updated bankroll looks like after a win based on clients "unit" amount
def risk_win
#bankroll_amount = #bankroll_amount + #risk_amount
#bankroll_amount.round(2)
evaluate_default_unit!.round(2)
end
# simulates what a clients updated bankroll looks like after a loss based on clients "unit" amount
def risk_loss
#bankroll_amount = #bankroll_amount - #risk_amount
evaluate_default_unit!
end
def evaluate_default_unit!
#risk_amount = #unit_percentage * #bankroll_amount.round(2)
end
end
Im not sure if this has anything to do with the fact that I am initializing these instance variables or not, but the #risk_amount returns the correct two decimal value, but when I return the object, the instance variable inside has running decimals.
c = Client.new 2000, 1
<Client:0x000001018956a0 #bankroll_amount=2000.0, #unit_percentage=0.01, #risk_amount=20.0>
c.risk_win
=> 20.2
When I run c.risk_win enough, it eventually returns
c
<Client:0x000001018956a0 #bankroll_amount=2440.3802, #unit_percentage=0.01, #risk_amount=24.4038>
This is one way to show only two decimal points.
price = 20.21340404
"%.2f" % price
# => 20.23
Also see RAILS number_to_currency helpers ActionView::Helpers::NumberHelper
http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_to_currency
Use number_with_precision to format the display of your floating point numbers to 2 decimal places:
number_with_precision(#bankroll_amount.to_f, precision: 2)
Usage in rails console:
[1] pry(main)> include ActionView::Helpers::NumberHelper
=> Object
[2] pry(main)> number_with_precision(2440.3802, precision: 2)
=> "2440.38"

Represent repeating decimals in Rails model

What's a good way to represent repeating decimals in the database?
Example 2.818181, the 81 repeats
Idea 1
Separate 2.818181 into non-repeating and repeating parts, then non_repeat = 2.0 and repeat = .007
class Decimal < ActiveRecord::Base
attr_accessible :non_repeat, :repeat #floats
def to_f
to_s.to_f
end
def to_s
"#{non_repeat + repeat}#{repeat.to_s.gsub(/0\./, '') * 3}" #approximation
end
def self.random_new
a = rand(100)
b = rand(100) / 100.0
self.new(non_repeat: a, repeat: b)
end
end
Idea 2
Use a fraction, which means turn 2.818181 into 31/11, save two integers 31 and 11
class Decimal < ActiveRecord::Base
attr_accessible :numerator, :denominator #integers
def to_f
numerator / denominator
end
def to_s
to_f.to_s
end
def self.random_new
a = rand(100)
b = random_prime(...) # like 7, 9, 11
self.new(numerator: a, denominator: b)
end
end
For the purpose of randomly generating repeating decimals, which idea is better? Or is there another way?
Your second approach won't always generate a repeating decimal number, just think what happens if a is a multiple of b.
The idea of using fractions tho is the best one. You need to slightly change your approach:
Randomly generate the integer part of your repeating number
Generate another random integer, rapresenting the repetition
Transform those 2 numbers into a fraction using the usual formula
rand = rand(100)
3.times { print rand.to_s }

Ruby on Rails field average?

Is there an easy way to obtain the average of an attribute in a collection?
For instance, each user has a score.
Given a collection of user(s) (#users), how can you get the average score for the group?
Is there anything like #users.average(:score)? I think I came across something like this for database fields, but I need it to work for a collection...
For your question, one could actually do:
#users.collect(&:score).sum.to_f/#users.length if #users.length > 0
Earlier I thought, #users.collect(&:score).average would have worked. For database fields, User.average(:score) will work. You can also add :conditions like other activerecord queries.
I use to extend our friend Array with this method:
class Array
# Calculates average of anything that responds to :"+" and :to_f
def avg
blank? and 0.0 or sum.to_f/size
end
end
Here's a little snippet to not only get the average but also the standard deviation.
class User
attr_accessor :score
def initialize(score)
#score = score
end
end
#users=[User.new(10), User.new(20), User.new(30), User.new(40)]
mean=#users.inject(0){|acc, user| acc + user.score} / #users.length.to_f
stddev = Math.sqrt(#users.inject(0) { |sum, u| sum + (u.score - mean) ** 2 } / #users.length.to_f )
u can use this here
http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html#method-i-average

Resources