Ruby: Convert dollar (String) to cents (Integer) - ruby

How do I convert a string with a dollar amount such as "5.32" or "100" to an integer amount in cents such as 532 or 10000?
I have a solution below:
dollar_amount_string = "5.32"
dollar_amount_bigdecimal = BigDecimal.new(dollar_amount_string)
cents_amount_bigdecimal = dollar_amount_bigdecimal * BigDecimal.new(100)
cents_amount_int = cents_amount_bigdecimal.to_i
but it seems wonky. I want to be sure because this will be an input to the PayPal API.
I've also tried the money gem, but it wasn't able to take strings as inputs.

You can use String#to_r ("to rational") to avoid round-off error.
def dollars_to_cents(dollars)
(100 * dollars.to_r).to_i
end
dollars_to_cents("12")
#=> 1200
dollars_to_cents("10.25")
#=> 1025
dollars_to_cents("-10.25")
#=> -1025
dollars_to_cents("-0")
#=> 0

d, c = dollar_amount_string.split(".")
d.to_i * 100 + c.to_i # => 532

I started with the original accepted answer, but had to make some important fixes along the way:
def dollars_to_cents(string=nil)
# remove all the signs and formatting
nums = string.to_s.strip.delete("$ CAD ,")
# add CENTS if they do not exit
nums = nums + ".00" unless nums.include?(".")
return (100 * nums.strip.to_r).to_i
end
So far works with these inputs:
CAD 1,215.92
CAD 1230.00
$11123.23
$123
43234.87
43,234.87

Related

Ruby Float Conversion to Cents

A user enters the string '67.99'. I need to ultimately convert this into the integer 6799.
In other words: convert the currency amount entered via a string into cents via integer data type.
I notice that this happens:
('67.99'.to_f * 100).to_i
#=> 6798
Not expected behavior. I need to save it as 6799, not 6798.
The issue is multiplying this float number by 100:
'67.99'.to_f * 100
#=> 6798.999999999999
Question: How can I properly convert a decimal, entered as a string, into an integer?
Example input and output:
'67' #=> 6700
'67.' #=> 6700
'67.9' #=> 6790
'67.99' #=> 6799
IMO: this is not a duplicate of this question because I am aware that float is not broken.
Use round:
('67.99'.to_f * 100).round
#=> 6799
As discussed in comments, there is a potentially better way to deal with such strings - BigDecimal class:
(BigDecimal.new('67.99') * 100).round
#=> 6799
This becomes relevant for large numbers:
input = '1000000000000001'
(input.to_f * 100).round
#=> 100000000000000096
(BigDecimal.new(input) * 100).round
#=> 100000000000000100

Adding together string-based minutes and seconds values

I have a a Track model that has a duration attribute. The attribute is string based, and reads in minutes:seconds format. I was wondering what the best way would be to take these string-based values and add them together. For example, if there are duration values like this:
Duration 1: "1:00"
Duration 2: "1:30"
how could I get it to output "2:30"?
Most of the questions I found related to this issue start with an integer based value. What's the best way to get this done?
My suggestion is to store/manipulate them as seconds.
It's definitely easier to store them as the integer number of seconds, and apply a function to parse/format the value into the proper string representation.
Storing them as integer will make it very easy to sum and subtract them.
Here is one way this can be done:
class Track
attr_accessor :duration
def initialize(duration)
#duration = duration
end
end
arr = [Track.new("1:00"), Track.new("1:30")]
total_seconds = arr.reduce(0) do |a, i|
min, sec = i.duration.split(":").map(&:to_i)
a + min * 60 + sec
end
p total_duration = '%d:%02d' % total_seconds.divmod(60)
#=> "2:30"
Edit: I missed #Wand's earlier answer, which is the same as mine. I'll leave mine just for the way I've organized the calculations.
arr = %w| 1:30 3:07 12:53 |
#=> ["1:30", "3:07", "12:53"]
"%d:%2d" % arr.reduce(0) do |tot,str|
m,s = str.split(':')
tot + 60*m.to_i + s.to_i
end.divmod(60)
#=> "17:30"
I just had to implement something like this in a recent project. Here is a simple start. If you are sure you will always have this format 'H:S', you will not need to convert your duration to time objects:
entries = ["01:00", "1:30", "1:45"]
hours = 0
minutes = 0
entries.each do |e|
entry_hours, entry_minutes = e.split(':', 2)
hours += entry_hours.to_i
minutes += entry_minutes.to_i
end
hours += (minutes/60)
minutes = (minutes%60)
p "#{hours}:#{minutes}"
#=> "4:15"
I agree with #SimoneCarletti: store them as an integer number of seconds. However, you could wrap them in a duration value class that can output itself as a nicely formatted string.
class Duration
attr_accessor :seconds
def initialize(string)
minutes, seconds = string.split(':', 2)
#seconds = minutes.to_i * 60 + seconds.to_i
end
def to_s
sprintf("%d:%02d", #seconds / 60, #seconds % 60)
end
def self.sum(*durations)
Duration.new(durations.reduce(0) { |t,d| t + d.seconds })
end
end
EDIT: Added a sum method similar to that suggested by #CarySwoveland below. You can use this as follows:
durations = ["1:30", "2:15"].map { |str| Duration.new(str) }
total = Duration.sum *durations

How to convert amount of time into seconds?

Say it took someone 3 minutes and 45 seconds to complete a task.
I'd represent that as 3:45.
But what I need to do is, assuming I'm given 3:45, convert that to the number of seconds it took.
So when given 3:45, I want to convert that to 225.
This would need to work with Ruby 1.8.7.
You could use something like Dave suggested, or if you need more stuff, there's a duration library that does this stuff.
It would look like:
001:0> ChronicDuration.parse("3:45")
225
I'd be careful about reinventing the wheel here. While you may assume you'll have only minutes and seconds, and that you'll always have the same format, it's safer to have something more robust.
Check out chronic_duration, a gem for parsing elapsed time.
def time_to_seconds(str)
time_in = []
time_in = str.split(":").reverse
asec = 0
secs = [1, 60, 60*60, 60*60*24]
time_in.size.times do {|i|
asec += secs[i].to_i * time_in[i].to_i
end
sec
end
class String
def to_secs
split(':').reverse.zip([1, 60, 60*60]).inject(0) { |m, e| m += e[0].to_i * e[1] }
end
end
puts '3:45'.to_secs # 225
puts '1:03:45'.to_secs # 3825
pry(main)> a = "3:45".split(":")
=> ["3", "45"]
pry(main)> a[0].to_i*60 + a[1].to_i
=> 225
(Wrapped up in a method, of course.)

how to convert a fraction to float in ruby

I have a string "1/16" I want to convert it to float and multiply it by 45. However, I dont get the desired results.
I am trying in script/console
>> "1/16".to_f
=> 1.0
>> "1/16".to_f*45
=> 45.0
how can i get the desired result of 2.81
Bigger picture:
I have a drop down like this:
<%=select_tag :volume, options_for_select(["", "1 g", "1/16 oz", "1/8 oz","1/4 oz",
"1/2 oz", "1 oz", "1/8 lb", "1/4 lb", "Single", "Multi 5" ], "N/A") %>
whenever user selects oz value then i want to multiply it to 45
so i do:
first, *rest = params[:volume].to_s.split(/ /)
if rest.first=="oz"
#indprodprice = #prods.orig_price.to_i*first.to_f*28.3495
else
#indprodprice = #prods.orig_price.to_i*first.to_f*453.59237
end
Use Rational
>> (Rational(*("1/16".split('/').map( &:to_i )))*45).to_f
=> 2.8125
Looks like you're going to have to parse the fraction yourself. This will work on fractions and whole numbers, but not mixed numbers (ie: 1½ will not work.)
class String
def to_frac
numerator, denominator = split('/').map(&:to_f)
denominator ||= 1
numerator/denominator
end
end
"1/16".to_frac * 45
#Farrel was right, and since Ruby 1.9 includes Rational and String has a to_r-method things are easier:
puts ("1/16".to_r * 45).to_f #=> 2.8125
puts ("1/16".to_r * 45).to_f.round(2) #=> 2.81
In 2.0 it became even easier with a rational literal:
1/16r # => (1/16)

How do I generate a random 10 digit number in ruby?

Additionally, how can I format it as a string padded with zeros?
To generate the number call rand with the result of the expression "10 to the power of 10"
rand(10 ** 10)
To pad the number with zeros you can use the string format operator
'%010d' % rand(10 ** 10)
or the rjust method of string
rand(10 ** 10).to_s.rjust(10,'0')
I would like to contribute probably a simplest solution I know, which is a quite a good trick.
rand.to_s[2..11]
=> "5950281724"
This is a fast way to generate a 10-sized string of digits:
10.times.map{rand(10)}.join # => "3401487670"
The most straightforward answer would probably be
rand(1e9...1e10).to_i
The to_i part is needed because 1e9 and 1e10 are actually floats:
irb(main)> 1e9.class
=> Float
DON'T USE rand.to_s[2..11].to_i
Why? Because here's what you can get:
rand.to_s[2..9] #=> "04890612"
and then:
"04890612".to_i #=> 4890612
Note that:
4890612.to_s.length #=> 7
Which is not what you've expected!
To check that error in your own code, instead of .to_i you may wrap it like this:
Integer(rand.to_s[2..9])
and very soon it will turn out that:
ArgumentError: invalid value for Integer(): "02939053"
So it's always better to stick to .center, but keep in mind that:
rand(9)
sometimes may give you 0.
To prevent that:
rand(1..9)
which will always return something withing 1..9 range.
I'm glad that I had good tests and I hope you will avoid breaking your system.
Random number generation
Use Kernel#rand method:
rand(1_000_000_000..9_999_999_999) # => random 10-digits number
Random string generation
Use times + map + join combination:
10.times.map { rand(0..9) }.join # => random 10-digit string (may start with 0!)
Number to string conversion with padding
Use String#% method:
"%010d" % 123348 # => "0000123348"
Password generation
Use KeePass password generator library, it supports different patterns for generating random password:
KeePass::Password.generate("d{10}") # => random 10-digit string (may start with 0!)
A documentation for KeePass patterns can be found here.
Just because it wasn't mentioned, the Kernel#sprintf method (or it's alias Kernel#format in the Powerpack Library) is generally preferred over the String#% method, as mentioned in the Ruby Community Style Guide.
Of course this is highly debatable, but to provide insight:
The syntax of #quackingduck's answer would be
# considered bad
'%010d' % rand(10**10)
# considered good
sprintf('%010d', rand(10**10))
The nature of this preference is primarily due to the cryptic nature of %. It's not very semantic by itself and without any additional context it can be confused with the % modulo operator.
Examples from the Style Guide:
# bad
'%d %d' % [20, 10]
# => '20 10'
# good
sprintf('%d %d', 20, 10)
# => '20 10'
# good
sprintf('%{first} %{second}', first: 20, second: 10)
# => '20 10'
format('%d %d', 20, 10)
# => '20 10'
# good
format('%{first} %{second}', first: 20, second: 10)
# => '20 10'
To make justice for String#%, I personally really like using operator-like syntaxes instead of commands, the same way you would do your_array << 'foo' over your_array.push('123').
This just illustrates a tendency in the community, what's "best" is up to you.
More info in this blogpost.
I ended up with using Ruby kernel srand
srand.to_s.last(10)
Docs here: Kernel#srand
Here is an expression that will use one fewer method call than quackingduck's example.
'%011d' % rand(1e10)
One caveat, 1e10 is a Float, and Kernel#rand ends up calling to_i on it, so for some higher values you might have some inconsistencies. To be more precise with a literal, you could also do:
'%011d' % rand(10_000_000_000) # Note that underscores are ignored in integer literals
('%010d' % rand(0..9999999999)).to_s
or
"#{'%010d' % rand(0..9999999999)}"
I just want to modify first answer. rand (10**10) may generate 9 digit random no if 0 is in first place. For ensuring 10 exact digit just modify
code = rand(10**10)
while code.to_s.length != 10
code = rand(11**11)
end
Try using the SecureRandom ruby library.
It generates random numbers but the length is not specific.
Go through this link for more information: http://ruby-doc.org/stdlib-2.1.2/libdoc/securerandom/rdoc/SecureRandom.html
Simplest way to generate n digit random number -
Random.new.rand((10**(n - 1))..(10**n))
generate 10 digit number number -
Random.new.rand((10**(10 - 1))..(10**10))
This technique works for any "alphabet"
(1..10).map{"0123456789".chars.to_a.sample}.join
=> "6383411680"
Just use straightforward below.
rand(10 ** 9...10 ** 10)
Just test it on IRB with below.
(1..1000).each { puts rand(10 ** 9...10 ** 10) }
To generate a random, 10-digit string:
# This generates a 10-digit string, where the
# minimum possible value is "0000000000", and the
# maximum possible value is "9999999999"
SecureRandom.random_number(10**10).to_s.rjust(10, '0')
Here's more detail of what's happening, shown by breaking the single line into multiple lines with explaining variables:
# Calculate the upper bound for the random number generator
# upper_bound = 10,000,000,000
upper_bound = 10**10
# n will be an integer with a minimum possible value of 0,
# and a maximum possible value of 9,999,999,999
n = SecureRandom.random_number(upper_bound)
# Convert the integer n to a string
# unpadded_str will be "0" if n == 0
# unpadded_str will be "9999999999" if n == 9_999_999_999
unpadded_str = n.to_s
# Pad the string with leading zeroes if it is less than
# 10 digits long.
# "0" would be padded to "0000000000"
# "123" would be padded to "0000000123"
# "9999999999" would not be padded, and remains unchanged as "9999999999"
padded_str = unpadded_str.rjust(10, '0')
rand(9999999999).to_s.center(10, rand(9).to_s).to_i
is faster than
rand.to_s[2..11].to_i
You can use:
puts Benchmark.measure{(1..1000000).map{rand(9999999999).to_s.center(10, rand(9).to_s).to_i}}
and
puts Benchmark.measure{(1..1000000).map{rand.to_s[2..11].to_i}}
in Rails console to confirm that.
An alternative answer, using the regexp-examples ruby gem:
require 'regexp-examples'
/\d{10}/.random_example # => "0826423747"
There's no need to "pad with zeros" with this approach, since you are immediately generating a String.
This will work even on ruby 1.8.7:
rand(9999999999).to_s.center(10, rand(9).to_s).to_i
A better approach is use Array.new() instead of .times.map. Rubocop recommends it.
Example:
string_size = 9
Array.new(string_size) do
rand(10).to_s
end
Rubucop, TimesMap:
https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Performance/TimesMap
In my case number must be unique in my models, so I added checking block.
module StringUtil
refine String.singleton_class do
def generate_random_digits(size:)
proc = lambda{ rand.to_s[2...(2 + size)] }
if block_given?
loop do
generated = proc.call
break generated if yield(generated) # check generated num meets condition
end
else
proc.call
end
end
end
end
using StringUtil
String.generate_random_digits(3) => "763"
String.generate_random_digits(3) do |num|
User.find_by(code: num).nil?
end => "689"(This is unique in Users code)
I did something like this
x = 10 #Number of digit
(rand(10 ** x) + 10**x).to_s[0..x-1]
Random 10 numbers:
require 'string_pattern'
puts "10:N".gen

Resources