How can I divide two table fields? - ruby-on-rails

This is in my model:
before_save :set_pay_this
def set_pay_this
self.pay_this = "#{self.amount} / #{self.divisor}"
end
end
But what gets saved as "pay this" is the "amount" entry, so no division is being done. Is it my syntax or what else should I post to ask my question properly?

"#{self.amount} / #{self.divisor}" is a string. If amount was 5 and divisor was 2 you'd end up with "5 / 2" the string, not the mathematical equation. You want this:
self.pay_this = self.amount / self.divisor
Or maybe this if you care about the cents and amount and divisor are integers.
self.pay_this = self.amount.to_f / self.divisor

Related

Return the value if the value exists

In Ruby on Rails, is there a better way to write this:
qty = 2
avg_price = room["price"].to_f if room["price"].present?
total_price = (avg_price * qty) if avg_price.present?
Especially the 2nd and 3rd line. I find myself using the if-else condition too often. Thanks.
You can try something like this:
qty = 2
total_price = room.fetch("price").to_f * qty
But there is a problem with this code, if there is no price field in the hash, it will raise an exception. Does it suffice your needs ?
It is hard to make this shorter, I would just do:
qty, avg_price, total_price = 2, nil, nil
if room["price"]
avg_price = Float(room["price"])
total_price = avg_price * qty
end
What about defining a helper method so that you can directly extract a float from a hash:
class Hash
def get_f key; fetch(key).to_f if key?(key) end # Or `if self[key].present?`
end
and then do:
qty = 2
avg_price = room.get_f("price")
total_price = avg_price * qty if avg_price
Perhaps a more object oriented approach? This approach makes it easier to test the code and might be reuseable.
class PriceCalculator
def init(quantity, price)
#quantity = quantity
#price = price.presence && price.to_f
end
def total
#price * #quantity if #price
end
end
total_price = PriceCalculator.new(2, room["price"]).total

Ruby: Average array of times

I have the following method in my Array class:
class Array
def avg
if partial_include?(":")
avg_times
else
blank? and 0.0 or (sum.to_f/size).round(2)
end
end
def avg_times
avg_minutes = self.map do |x|
hour, minute = x.split(':')
total_minutes = hour.to_i * 60 + minute.to_i
end.inject(:+)/size
"#{avg_minutes/60}:#{avg_minutes%60}"
end
def partial_include?(search_term)
self.each do |e|
return true if e[search_term]
end
return false
end
end
This works great with arrays of regular numbers, but there could instances where I have an array of times.
For example: [18:35, 19:07, 23:09]
Anyway to figure out the average of an array of time objects?
So you need do define a function that can calculate the average of times formatted as strings. Convert the data to minutes, avg the total minutes and then back to a time.
I would do it something like this:
a = ['18:35', '19:07', '23:09']
def avg_of_times(array_of_time)
size = array_of_time.size
avg_minutes = array_of_time.map do |x|
hour, minute = x.split(':')
total_minutes = hour.to_i * 60 + minute.to_i
end.inject(:+)/size
"#{avg_minutes/60}:#{avg_minutes%60}"
end
p avg_of_times(a) # = > "20:17"
Then when you call you function you check if any/all items in your array is formatted as a time. Maybe using regexp.
Average the Hours and Minutes Separately
Here's a simple method that we're using:
def calculate_average_of_times( times )
hours = times.collect{ |time| time.split( ":" ).first.to_i } # Large Arrays should only
minutes = times.collect{ |time| time.split( ":" ).second.to_i } # call .split 1 time.
average_hours = hours.sum / hours.size
average_minutes = ( minutes.sum / minutes.size ).to_s.rjust( 2, '0' ) # Pad with leading zero if necessary.
"#{ average_hours }:#{ average_minutes }"
end
And to show it working with your provided Array of 24-hour times, converted to Strings:
calculate_average_of_times( ["18:35", "19:07", "23:09"] )
#=> "20:17"
Thanks to #matt-privman for the help and inspiration on this.

Choosing which form field to submit

I have a CommercialDocument model which have a discount_amount attribute and a discount_amount_with_tax virtual attribute.
Here is how I defined this in my model :
def discount_amount_with_tax
discount_amount * (1 + tax.rate / 100)
end
def discount_amount_with_tax=(amount)
self.discount_amount = amount.to_f / (1 + tax.rate / 100)
end
In my form, a user can fill in both discount_amount and discount_amount_tax :
= f.label :discount_amount
= f.text_field :discount_amount
= f.text_field :discount_amount_with_tax
I want to give the priority to the discount_amount_with_tax field, which means that discount_amount must not be taken into account unless the other field is empty.
My problem is that if I put nothing in the discount_amount_with_tax field, and let's say 10 in discount_amount, then discount_amount will be equal to 0, which is clearly not what I want.
How can I fix this ?
Any help would be greatly appreciated.
"".to_i
# => 0
A blank string converts to a zero integer. Therefore:
def discount_amount_with_tax=(amount)
self.discount_amount = amount.to_f / (1 + tax.rate / 100)
end
# same as...
def discount_amount_with_tax=(0)
self.discount_amount = 0 / (...)
end
# 0 / anything except zero = 0
# self.discount_amount = 0 no matter what
During mass-assignment, discount_amount_with_tax= is called. A blank form input is passed as an empty string, which Active Record then converts to an integer (zero). discount_amount_with_tax= sets discount_amount to zero regardless of discount_amount's previous value.
Easy way around this is to use a conditional:
def discount_amount_with_tax=(amount)
self.discount_amount = (amount.to_f / (1 + tax.rate / 100)) if amount > 0
end
Mind you, this is the easy way, not the ideal way. A better solution is to write custom setter logic in the controller in lieu of mass-assignment; basically to manually set these attributes in the controller.
I think you can use a before_validation callback here to set the discount_amount field.
before_validation :calculate_discount_amount
def discount_amount_with_tax
#discount_amount_with_tax ||= discount_amount * (1 + tax.rate / 100.0)
end
def discount_amount_with_tax=(amt)
#discount_amount_with_tax = amt
end
def calculate_discount_amount
self.discount_amount = discount_amount_with_tax / (1 + tax.rate / 100.0)
end
Just a reminder that you need to use 100.0 instead of 100 so that you'll be dividing by float and not by integer.

How do I initialize a primitive, and add to it?

I'm trying to initialize a variable as a float and then add to it in a times loop. Here's my code:
amt = 0.0
5.times do
amt = amt + ("%.2f" % (item.price * 0.05))
end
Here's the error:
String can't be coerced into Float
EDIT:
I've changed the 1.times do. I had adapted my code so that it would be more readable, the 1.times wasn't in my actual code.
Firstly, try not to use floats for money - what you'll need to do in this case is use BigDecimal - floating point math is not accurate.
To use BigDecimal, require 'bigdecimal' in your application.rb
Then, what you want is:
amt = BigDecimal.new("0")
1.times do
price = BigDecimal.new(item.price.to_s)
factor = BigDecimal.new("0.05")
amt += (price * factor).round(2)
end
amt
Yeah sorry everyone. I am new to Ruby so I didn't know that this was the problem, but the "%.2f" was causing the problem. (Although irrelevant to this question, what this does is convert the amount into two decimal precision.) Stupid mistake!

LocalJumpError (Ruby on Rails)

I have searched/Googled around but I'm struggling with the following problem.
I am building a Rails 2.3.2 application and one of the requirements is to calculate the median of an array of results. I am using code for calculating the median from the Ruby Cookbook but keep running in to a problem with receiving an error 'LocalJumpError - no block given' when I attempt to find the median of an array where there are an odd number of members.
The example code in my view is as follows:
<%= survey_response.median([6,4,5,4,4,2]) %>
Then in survey_response.rb model the methods are as follows:
def mean(array)
array.inject(array.inject(0) { |sum, x| sum += x } / array.size.to_f)
end
def median(array,already_sorted=false)
return nil if array.empty?
array = array.sort unless already_sorted
m_pos = array.size / 2
return array.size % 2 == 1 ? array[m_pos] : mean(array[m_pos-1..m_pos])
end
The error is caused when the median method refers back to the mean method to get the media of an odd total of items in the array. I just can't figure out why I get that error or indeed how to fix it - so I'd hugely appreciate any help/guidance/laughing anybody could offer me!
Thanks
Simon
Lis looks like it's due to you using a fractional index into the array. Try replacing:
m_pos = array.size / 2
with:
m_pos = (array.size / 2).ceil
Also, try changing your mean function to this:
def mean(array)
array.inject(0) { |sum, x| sum += x } / array.size.to_f
end
That mean method looks horribly botched. Try this:
def mean(array)
a.inject(0) { |sum,x| sum += x } / a.size.to_f
end
Better code:
def mean(array)
array.inject { |sum, n| sum + n } / array.length.to_f
end
def median(array)
return nil if array.empty?
array.sort!
middle = array.length / 2
(array.length % 2 == 1) ? array[middle] : mean([array[middle-1], array[middle]])
end
puts median([5,11,12,4,8,21]) # => 9.5

Resources