Looking to work with a dataset of strings that store money amounts in these formats. For example:
$217.3M
$1.6B
$34M
€1M
€2.8B
I looked at the money gem but it doesn't look like it handles the "M, B, k"'s back to numbers. Looking for a gem that does do that so I can convert exchange rates and compare quantities. I need the opposite of the number_to_human method.
I would start with something like this:
MULTIPLIERS = { 'k' => 10**3, 'm' => 10**6, 'b' => 10**9 }
def human_to_number(human)
number = human[/(\d+\.?)+/].to_f
factor = human[/\w$/].try(:downcase)
number * MULTIPLIERS.fetch(factor, 1)
end
human_to_number('$217.3M') #=> 217300000.0
human_to_number('$1.6B') #=> 1600000000.0
human_to_number('$34M') #=> 34000000.0
human_to_number('€1M') #=> 1000000.0
human_to_number('€2.8B') #=> 2800000000.0
human_to_number('1000') #=> 1000.0
human_to_number('10.88') #=> 10.88
I decided to not be lazy and actually write my own function if anyone else wants this:
def text_to_money(text)
returnarray = []
if (text.count('k') >= 1 || text.count('K') >= 1)
multiplier = 1000
elsif (text.count('M') >= 1 || text.count('m') >= 1)
multiplier = 1000000
elsif (text.count('B') >= 1 || text.count('b') >= 1)
multiplier = 1000000000
else
multiplier = 1
end
num = text.to_s.gsub(/[$,]/,'').to_f
total = num * multiplier
returnarray << [text[0], total]
return returnarray
end
Thanks for the help!
Forgive my lack of code but I can't quite figure out the best way to achieve the following:
two strings (stored as strings because of the leading 0 - they are phone numbers) :
a = '0123456700'
b = '0123456750'
I am trying to find a way to write them as a range as follows
0123456700 - 750
rather than
0123456700 - 0123456750
which I currently have.
It's not as straightforward as getting the last 3 digits of b since the range can vary and perhaps go up to 4 digits so I'm trying to find the best way of being able to do this.
I'd look up the index of the first unequal pair of characters:
a = '0123456700'
b = '0123456750'
index = a.chars.zip(b.chars).index { |x, y| x != y }
#=> 8
And extract the suffix with:
"#{a} - #{b[index..-1]}" if index
#=> "0123456700 - 50"
Here's a method that returns the range:
def my_range(a, b)
a = a.delete(" ") # remove all spaces from string
b = b.delete(" ")
a, b = b, a if a.to_i > b.to_i # a is always smaller than b
ai, bi = a.to_i, b.to_i
pow = 1
while ai > 1
pow += 1
len = pow if ai % 10 != bi % 10
ai /= 10
bi /= 10
end
a + " - " + b[-len..-1]
end
puts my_range("0123456700", "0123456750") # 0123456700 - 750
puts my_range("0123456669", "0123456675") # 0123456669 - 675
puts my_range("0123400200", "0123500200") # 0123400200 - 3500200
puts my_range("012 345 678", "01 235 0521") # 012345678 - 350521
From my personal library (simplified):
def common_prefix first, second
i = 0
loop{break unless first[i] and second[i] == first[i]; i += 1}
first[0, i]
end
a = "0123456700"
b = "0123456750"
c = "0123457750"
common_prefix(a, b)
# => "01234567"
"#{a} - #{b.sub(common_prefix(a, b), "")}"
# => "0123456700 - 50"
"#{a} - #{c.sub(common_prefix(a, c), "")}"
# => "0123456700 - 7750"
Note. This will work correctly only under the assumption that all strings are right padded with 0 to be the same length.
How to convert numbers to words in ruby?
I know there is a gem somewhere. Trying to implement it without a gem. I just need the numbers to words in English for integers. Found this but it is very messy. If you have any idea on how to implement a cleaner easier to read solution please share.
http://raveendran.wordpress.com/2009/05/29/ruby-convert-number-to-english-word/
Here is what I have been working on. But having some problem implementing the scales. The code is still a mess. I hope to make it more readable when it functions properly.
class Numberswords
def in_words(n)
words_hash = {0=>"zero",1=>"one",2=>"two",3=>"three",4=>"four",5=>"five",6=>"six",7=>"seven",8=>"eight",9=>"nine",
10=>"ten",11=>"eleven",12=>"twelve",13=>"thirteen",14=>"fourteen",15=>"fifteen",16=>"sixteen",
17=>"seventeen", 18=>"eighteen",19=>"nineteen",
20=>"twenty",30=>"thirty",40=>"forty",50=>"fifty",60=>"sixty",70=>"seventy",80=>"eighty",90=>"ninety"}
scale = [000=>"",1000=>"thousand",1000000=>" million",1000000000=>" billion",1000000000000=>" trillion", 1000000000000000=>" quadrillion"]
if words_hash.has_key?(n)
words_hash[n]
#still working on this middle part. Anything above 999 will not work
elsif n>= 1000
print n.to_s.scan(/.{1,3}/) do |number|
print number
end
#print value = n.to_s.reverse.scan(/.{1,3}/).inject([]) { |first_part,second_part| first_part << (second_part == "000" ? "" : second_part.reverse.to_i.in_words) }
#(value.each_with_index.map { |first_part,second_part| first_part == "" ? "" : first_part + scale[second_part] }-[""]).reverse.join(" ")
elsif n <= 99
return [words_hash[n - n%10],words_hash[n%10]].join(" ")
else
words_hash.merge!({ 100=>"hundred" })
([(n%100 < 20 ? n%100 : n.to_s[2].to_i), n.to_s[1].to_i*10, 100, n.to_s[0].to_i]-[0]-[10])
.reverse.map { |num| words_hash[num] }.join(" ")
end
end
end
#test code
test = Numberswords.new
print test.in_words(200)
My take on this
def in_words(int)
numbers_to_name = {
1000000 => "million",
1000 => "thousand",
100 => "hundred",
90 => "ninety",
80 => "eighty",
70 => "seventy",
60 => "sixty",
50 => "fifty",
40 => "forty",
30 => "thirty",
20 => "twenty",
19=>"nineteen",
18=>"eighteen",
17=>"seventeen",
16=>"sixteen",
15=>"fifteen",
14=>"fourteen",
13=>"thirteen",
12=>"twelve",
11 => "eleven",
10 => "ten",
9 => "nine",
8 => "eight",
7 => "seven",
6 => "six",
5 => "five",
4 => "four",
3 => "three",
2 => "two",
1 => "one"
}
str = ""
numbers_to_name.each do |num, name|
if int == 0
return str
elsif int.to_s.length == 1 && int/num > 0
return str + "#{name}"
elsif int < 100 && int/num > 0
return str + "#{name}" if int%num == 0
return str + "#{name} " + in_words(int%num)
elsif int/num > 0
return str + in_words(int/num) + " #{name} " + in_words(int%num)
end
end
end
puts in_words(4) == "four"
puts in_words(27) == "twenty seven"
puts in_words(102) == "one hundred two"
puts in_words(38_079) == "thirty eight thousand seventy nine"
puts in_words(82102713) == "eighty two million one hundred two thousand seven hundred thirteen"
Have you considered humanize ?
https://github.com/radar/humanize
Simple answer use humanize gem and you will get desired output
Install it directly
gem install humanize
Or add it to your Gemfile
gem 'humanize'
And you can use it
require 'humanize'
1.humanize #=> 'one'
345.humanize #=> 'three hundred and forty-five'
1723323.humanize #=> 'one million, seven hundred and twenty-three thousand, three hundred and twenty-three'
If you are using this in rails you can directly use this
NOTE: As mentioned by sren in the comments below. The humanize method provided by ActiveSupport is different than the gem humanize
You can also use the to_words gem.
This Gem converts integers into words.
e.g.
1.to_words # one ,
100.to_words # one hundred ,
101.to_words # one hundred and one
It also converts negative numbers.
I can see what you're looking for, and you may wish to check out this StackOverflow post: Number to English Word Conversion Rails
Here it is in summary:
No, you have to write a function yourself. The closest thing to what
you want is number_to_human, but that does not convert 1 to One.
Here are some URLs that may be helpful:
http://codesnippets.joyent.com/posts/show/447
http://raveendran.wordpress.com/2009/05/29/ruby-convert-number-to-english-word/
http://deveiate.org/projects/Linguistics/
I am not quite sure, if this works for you. Method can be called like this.
n2w(33123) {|i| puts i unless i.to_s.empty?}
Here is the method ( I have not tested it fully. I think it works upto million. Code is ugly, there is a lot of room for re-factoring. )
def n2w(n)
words_hash = {0=>"zero",1=>"one",2=>"two",3=>"three",4=>"four",5=>"five",6=>"six",7=>"seven",8=>"eight",9=>"nine",
10=>"ten",11=>"eleven",12=>"twelve",13=>"thirteen",14=>"fourteen",15=>"fifteen",16=>"sixteen",
17=>"seventeen", 18=>"eighteen",19=>"nineteen",
20=>"twenty",30=>"thirty",40=>"forty",50=>"fifty",60=>"sixty",70=>"seventy",80=>"eighty",90=>"ninety"}
scale = {3=>"hundred",4 =>"thousand",6=>"million",9=>"billion"}
if words_hash.has_key?n
yield words_hash[n]
else
ns = n.to_s.split(//)
while ns.size > 0
if ns.size == 2
yield("and")
yield words_hash[(ns.join.to_i) - (ns.join.to_i)%10]
ns.shift
end
if ns.size > 4
yield(words_hash[(ns[0,2].join.to_i) - (ns[0,2].join.to_i) % 10])
else
yield(words_hash[ns[0].to_i])
end
yield(scale[ns.size])
ns.shift
end
end
end
def subhundred number
ones = %w{zero one two three four five six seven eight nine
ten eleven twelve thirteen fourteen fifteen
sixteen seventeen eighteen nineteen}
tens = %w{zero ten twenty thirty **forty** fifty sixty seventy eighty ninety}
subhundred = number % 100
return [ones[subhundred]] if subhundred < 20
return [tens[subhundred / 10]] if subhundred % 10 == 0
return [tens[subhundred / 10], ones[subhundred % 10]]
end
def subthousand number
hundreds = (number % 1000) / 100
tens = number % 100
s = []
s = subhundred(hundreds) + ["hundred"] unless hundreds == 0
s = s + ["and"] unless hundreds == 0 or tens == 0
s = s + [subhundred(tens)] unless tens == 0
end
def decimals number
return [] unless number.to_s['.']
digits = number.to_s.split('.')[1].split('').reverse
digits = digits.drop_while {|d| d.to_i == 0} . reverse
digits = digits.map {|d| subhundred d.to_i} . flatten
digits.empty? ? [] : ["and cents"] + digits
end
def words_from_numbers number
steps = [""] + %w{thousand million billion trillion quadrillion quintillion sextillion}
result = []
n = number.to_i
steps.each do |step|
x = n % 1000
unit = (step == "") ? [] : [step]
result = subthousand(x) + unit + result unless x == 0
n = n / 1000
end
result = ["zero"] if result.empty?
result = result + decimals(number)
result.join(' ').strip
end
def words_from_numbers(number)
ApplicationHelper.words_from_numbers(number)
end
Its been quite a while since the question was asked. Rails has something inbuilt for this now.
https://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html
number_to_human(1234567) # => "1.23 Million"
number_to_human(1234567890) # => "1.23 Billion"
number_to_human(1234567890123) # => "1.23 Trillion"
number_to_human(1234567890123456) # => "1.23 Quadrillion"
number_to_human(1234567890123456789) # => "1230 Quadrillion"
I'm pretty new to Rails, I'm passing 2 variables to a view, in my controller have them defined like
#correct = 5
#total = 40
in my view I'm trying to mark them up like:
Score = <%=#score%>/<%=#total%> = <%=(#score/#total)%>
It outputs 0 for the division. Do I need to explicitly define that equation to output a float or something? How do I get it to output 0.125 instead of 0
Thanks guys
Decide whether you want to use #correct or #score. Also to use double division instead of integer multiply #score by 1.0:
<%=(1.0 * #score/#total)%>
Or alternatively cast #score to float:
<%=(#score.to_f/#total)%>
You need to explicitly convert your integers into floats:
1.9.3p0 :001 > a = 5
=> 5
1.9.3p0 :002 > b = 40
=> 40
1.9.3p0 :003 > a / b
=> 0
1.9.3p0 :005 > (a / b).to_f
=> 0.0
1.9.3p0 :006 > a.to_f / b.to_f
=> 0.125
In your case:
Score = <%=#score%>/<%=#total%> = <%=(#score.to_f/#total.to_f)%>
Float round rounds it up or down. I always need it to round down.
I have the solution but i dont really like it... Maybe there is a better way.
This is what i want:
1.9999.round_down(2)
#=> 1.99
1.9901.round_down(2)
#=> 1
I came up with this solution but i would like to know if there is a better solution(I dont like that i convert the float twice). Is there already a method for this? Because I found it pretty strange that I couldnt find it.
class Float
def round_down(n=0)
((self * 10**n).to_i).to_f/10**n
end
end
Thanks.
1.9999.to_i
#=> 1
1.9999.floor
#=> 1
answered 1 sec ago fl00r
"%.2f" % 1.93213
#=> 1.93
#kimmmo is right.
class Float
def round_down(n=0)
self.to_s[/\d+\.\d{#{n}}/].to_f
end
end
Based on answer from #kimmmo this should be a little more efficient:
class Float
def round_down n=0
s = self.to_s
l = s.index('.') + 1 + n
s.length <= l ? self : s[0,l].to_f
end
end
1.9991.round_down(3)
=> 1.999
1.9991.round_down(2)
=> 1.99
1.9991.round_down(0)
=> 1.0
1.9991.round_down(5)
=> 1.9991
or based on answer from #steenslag, probably yet more efficient as there is no string conversion:
class Float
def round_down n=0
n < 1 ? self.to_i.to_f : (self - 0.5 / 10**n).round(n)
end
end
Looks like you just want to strip decimals after n
class Float
def round_down(n=0)
int,dec=self.to_s.split('.')
"#{int}.#{dec[0...n]}".to_f
end
end
1.9991.round_down(3)
=> 1.999
1.9991.round_down(2)
=> 1.99
1.9991.round_down(0)
=> 1.0
1.9991.round_down(10)
=> 1.9991
(Edit: slightly more efficient version without the regexp)
You could use the floor method
http://www.ruby-doc.org/core/classes/Float.html#M000142
For anybody viewing this question in modern times (Ruby 2.4+), floor now accepts an argument.
> 1.9999.floor(1)
=> 1.9
> 1.9999.floor(2)
=> 1.99
> 1.9999.floor(3)
=> 1.999
> 1.9999.ceil(2)
=> 2.0
In Ruby 1.9:
class Float
def floor_with_prec(prec = 0)
(self - 0.5).round(prec)
end
end
class Float
def rownd_down(digits = 1)
("%.#{digits+1}f" % self)[0..-2].to_f
end
end
> 1.9991.rownd_down(3)
=> 1.999
> 1.9991.rownd_down(2)
=> 1.99
> 1.9991.rownd_down(10)
> 1.9991
Found a bug for the answers that try to calculate float in round_down method.
> 8.7.round_down(1)
=> 8.7
> 8.7.round_down(2)
=> 8.69
you can use bigdecimal, integer or maybe string to do all the math but float.
> 8.7 - 0.005
=> 8.694999999999999
Here is my solution:
require 'bigdecimal'
class Float
def floor2(n = 0)
BigDecimal.new(self.to_s).floor(n).to_f
end
end
> 8.7.floor2(1)
=> 8.7
> 8.7.floor2(2)
=> 8.7
> 1.9991.floor(3)
=> 1.999
> 1.9991.floor(2)
=> 1.99
> 1.9991.floor(1)
=> 1.9
class Float
def round_down(n)
num = self.round(n)
num > self ? (num - 0.1**n) : num
end
end
56.0.round_down(-1) = 50. Works with negative numbers as well, if you agree that rounding down makes a number smaller: -56.09.round_down(1) = -56.1.
Found this article helpful: https://richonrails.com/articles/rounding-numbers-in-ruby
Here are the round up and down methods:
class Float
def round_down(exp = 0)
multiplier = 10 ** exp
((self * multiplier).floor).to_f/multiplier.to_f
end
def round_up(exp = 0)
multiplier = 10 ** exp
((self * multiplier).ceil).to_f/multiplier.to_f
end
end
This worked for me.
> (1.999).to_i.to_f
For rounding up you could just use
> (1.999+1).to_i.to_f