Convert string numbers( in word format) to integer ruby - ruby

If you have a string ten, is it possible to convert it to an integer 10 in Ruby? (maybe in rails?)
I value the developers at tryruby.org, and in their tutorial here, it specifically says "to_i converts things to integers (numbers.)" I am wondering why they didn't say "to_i converts STRINGS to integers (numbers.)"
What variable types can be converted from their type to an integer?

Check out this gem for handling word to number conversions.
From the readme:
require 'numbers_in_words'
require 'numbers_in_words/duck_punch'
112.in_words
#=> one hundred and twelve
"Seventy million, five-hundred and fifty six thousand point eight nine three".in_numbers
#=> 70556000.893

How I would have done it.
def n_to_s(int)
set1 = ["","one","two","three","four","five","six","seven",
"eight","nine","ten","eleven","twelve","thirteen",
"fourteen","fifteen","sixteen","seventeen","eighteen",
"nineteen"]
set2 = ["","","twenty","thirty","forty","fifty","sixty",
"seventy","eighty","ninety"]
thousands = (int/1000)
hundreds = ((int%1000) / 100)
tens = ((int % 100) / 10)
ones = int % 10
string = ""
string += set1[thousands] + " thousand " if thousands != 0 if thousands > 0
string += set1[hundreds] + " hundred" if hundreds != 0
string +=" and " if tens != 0 || ones != 0
string = string + set1[tens*10+ones] if tens < 2
string += set2[tens]
string = string + " " + set1[ones] if ones != 0
string << 'zero' if int == 0
p string
end
for the purpose of testing;
n_to_s(rand(9999))

Since String#to_i picks out only the number characters, it will not work in the way you want. There may be some Rails method related to that, but it surely will not have the method name to_i because its behavior will conflict with the original intent of String#to_i.
It is not only Strings that has to_i. NilClass, Time, Float, Rational (and perhaps some other classes) do as well.
"3".to_i #=> 3
"".to_i #=> 0
nil.to_i #=> 0
Time.now.to_i #=> 1353932622
(3.0).to_i #=> 3
Rational(10/3).to_i #=> 3

This is a simple lookup of strings to their numeric equivalent:
str_to_int_hash = {
'zero' => 0,
'one' => 1,
'two' => 2,
'three' => 3,
'four' => 4,
'five' => 5,
'six' => 6,
'seven' => 7,
'eight' => 8,
'nine' => 9,
'ten' => 10
}
str_to_int_hash['ten']
=> 10
It's obvious there are many other missing entries, but it illustrates the idea.
If you want to go from a number to the string, this is the starting point:
int_to_str_hash = Hash[str_to_int_hash.map{ |k,v| [v,k] }]
int_to_str_hash[10]
=> "ten"

Related

Roman Numerals Decoder: Ruby

Alright I've been working on this coding challenge for quite some time and I guess it's officially time for me to raise the flag. Help!
My task is to create a function that takes a Roman numeral as its argument and returns its value as a numeric decimal integer.
So far I've successfully created a hash mapping the numbers to its numeric values. I've also created an empty array roman_no to pass the key/value pair through.
What I am struggling with is writing the expression. Below is the full code:
def solution(roman)
# take a value of a roman numeral
roman_numeral =
{
1000 => "M",
900 => "CM",
500 => "D",
400 => "CD",
100 => "C",
90 => "XC",
50 => "L",
40 => "XL",
10 => "X",
9 => "IX",
5 => "V",
4 => "IV",
1 => "I"
}
roman_no = Array.new
roman_numeral.each do | key, value |
while
"#{roman}" >= "#{key}"
+= roman_no
"#{roman}" -= "#{key}"
end
return roman_no
solution('XXI')
How can I write an argument that will take the value from roman_numeral and return its number counter part?
for example:
solution('XXI') # should return 21
def solution(roman)
mapping = {
"M"=>1000,
"D"=>500,
"C"=>100,
"L"=>50,
"X"=>10,
"V"=>5,
"I"=>1
}
# split string into characters
roman.chars.map do |l|
mapping[l] # replace character with integer value
end
.compact # removes potential nils caused by invalid chars
# Splits array into chunks so that we can handle numerals such as IIX
.chunk_while do |i,j|
i <= j #
end
# each chunk will be an array like [10, 10, 100] or [1, 1, 1, 1]
.map do |chunk|
if chunk.first < chunk.last
chunk.reverse.inject(:-) # handles numerals such as IIX with subtraction
else
chunk.sum # chunk is just a list of numerals such as III
end
end
.sum # sums everything up
end

Compress a number as a string to fit in 256 char space

I'm trying to use a bitmask to provide as many binary values as possible so that the final value will store in the limited allocate memory for a string. My current methodology is to find a maximum number and convert it to a string base-36.
value = (0 | (1<<1318)).to_s(36)
The result is 255 chars of a compressed number from which I can extract my original number of 1318. The downside is I'm limited to 1,318 binary values and I want to expand that number. Are there any alternative strategies in Ruby to compress this number even further?
You can always encode your number into base s and then represent that as string with whatever alphabet you want.
def encode(n, alphabet)
s = alphabet.size
res = []
while (n > 0)
res << n % s
n = n / s
end
res.reverse.map { |i| alphabet[i] }.join
end
Your method is then equivalent to encode(n, alphabet), where alphabet is defined as
alphabet = ((0..9).to_a + ("a".."z").to_a).join
# => "0123456789abcdefghijklmnopqrstuvwxyz"
But you might as well use all possible characters instead of only 36 of them:
extended_alphabet = (0..255).map { |i| i.chr }.join
This gives a total of (256 ** 255) possibilities, i.e. up to (2 ** 2040), which is much better than your actual (2 ** 1318).
This encoding happens to be optimal because each character of your string can have at most 256 different values, and all of them are used here.
Decoding can then be performed as follows:
def decode(encoded, alphabet)
s = alphabet.size
n = 0
decode_dict = {}; i = -1
alphabet.each_char { |c| decode_dict[c] = (i += 1) }
encoded.each_char do |c|
n = n * s + decode_dict[c]
end
n
end
If you are going to use a fixed alphabet for all your encodings, I would suggest computing the decoding dictionnary outside of the function and taking it as a parameter instead of alphabet, to avoid computing it every time you try to encode a number.
Numbers are non-negative
If the numbers are non-negative we can encode each 8-bits of each number to a character that is part of a string, and then decode the string by converting each character to 8 bits of the number.
def encode(n)
str = ''
until n.zero?
str << (n & 255).chr
n = n >> 8
end
str.reverse
end
def decode(str)
str.each_char.reduce(0) { |n,c| (n << 8) | c.ord }
end
This uses the following bit-manipulation methods in the class Integer: &, >>, << and |.
def test(n)
encoded = encode(n)
puts "#{n} => #{encoded} => #{decode(encoded)}"
end
test 1 # 1 => ["\u0001"] => 1
test 63 # 63 => ["?"] => 63
test 64 # 64 => ["#"] => 64
test 255 # 255 => ["\xFF"] => 255
test 256 # 256 => ["\u0001", "\u0000"] => 256
test 123456 # 123456 => ["\x01", "\xE2", "#"] => 123456
For example,
n = 123456
n.to_s(2)
#=> "11110001001000000"
so
n = 0b11110001001000000
#=> 123456
The bytes of this number can be visualized so:
00000001 11100010 01000000
We see that
a = [0b00000001, 0b11100010, 0b01000000]
a.map(&:chr)
#=> ["\x01", "\xE2", "#"]
Numbers can be negative
If the numbers to be encoded can be negative we need to first convert then to their absolute values then add some information to the encoded string that indicates whether they are non-negative or negative. That will require at least one additional byte so we might include a "+" for non-negative numbers and a "-" for negative numbers.
def encode(n)
sign = "+"
if n < 0
sign = "-"
n = -n
end
str = ''
until n.zero?
str << (n & 255).chr
n = n >> 8
end
(str << sign).reverse
end
def decode(str)
n = str[1..-1].each_char.reduce(0) { |n,c| (n << 8) | c.ord }
str[0] == '+' ? n : -n
end
test -255 # -255 => ["-", "\xFF"] => -255
test -256 # -256 => ["-", "\u0001", "\u0000"] => -256
test -123456 # -123456 => ["-", "\x01", "\xE2", "#"] => -123456
test 123456 # 123456 => ["+", "\x01", "\xE2", "#"] => 123456

Project Euler 8 in Ruby

I know my code works to get the correct answer for 4 adjacent integers. But it's not working with 13.
The only thing I can think of is that it can be an issue with an unsigned int, but in Ruby I don't think I'd have that problem because it would change automatically into a Bignum class.
So that means that somewhere in my calculation I am wrong?
Please give me a hint.
# Euler 8
# http://projecteuler.net/index.php?section=problems&id=8
# Find the thirteen adjacent digits in the 1000-digit number
# that have the greatest product.
# What is the value of this product?
number = []
#split the integer as a string into an array
long_digit = "73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450"
long_digit.split("").map { |s| number << s.to_i }
#iterate through the array to find the 13 ajacent digits that have the largest product
largest_product = 0
a = 0
#stay within the bounds of the array
while number[a+12]
current_product = number[a] * number[a+1] * number[a+2] * number[a+3] * number[a+4] * number[a+5] * number[a+6] * number[a+7] * number[a+8] * number[a+9] * number[a+10] * number[a+11] * number[a+12]
if current_product > largest_product
largest_product = current_product
end
a = a + 1
end
puts largest_product
I think this solution is pretty clean and simple:
#!/usr/bin/env ruby
input = "
73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450"
.gsub(/\s+/, '')
puts input.chars
.map(&:to_i)
.each_cons(13)
.map { |seq| seq.reduce(:*) }
.max
gsub performs the trimming.
chars gets the characters.
map(&:to_i) maps all the chars to ints.
each_cons(13) gets blocks of consecutive numbers (https://ruby-doc.org/core-2.4.1/Enumerable.html#method-i-each_cons)
map { |seq| seq.reduce(:*) } is going to take each of the consecutive blocks and perform a reduce (multiplying all the numbers of each slice/consecutive block of numbers).
max gets the maximum value.
Issue seems to be due to lot of white space chars in the string long_digit that are become 0 in the array number, thus giving wrong results.
Here is a corrected and simplified version. After removing newlines and spaces using gsub, we now have a 1000 digit number and we get correct answer.
number = long_digit.gsub!(/\s/, '').split("").map{ |s| s.to_i }
n = 13
p number.each_cons(n).map{|a| a.reduce {|a, i| a = a * i }}.max
#=> 23514624000
First, let's fix the string:
long_digit.gsub!(/\s|\n/,'')
long_digit.size #=> 1000
We can speed this up by eliminating 13-character substrings that contain a zero:
shorter_digit_arr = long_digit.split('0').reject { |s| s.size < 13 }
#=> ["7316717653133",
# "6249192251196744265747423553491949349698352",
# "6326239578318",
# "18694788518438586156",
# "7891129494954595",
# "17379583319528532",
# "698747158523863",
# "435576689664895",
# "4452445231617318564",
# "987111217223831136222989342338",
# "81353362766142828",
# "64444866452387493",
# "1724271218839987979",
# "9377665727333",
# "594752243525849",
# "632441572215539753697817977846174",
# "86256932197846862248283972241375657",
# "79729686524145351",
# "6585412275886668811642717147992444292823",
# "863465674813919123162824586178664583591245665294765456828489128831426",
# "96245544436298123",
# "9878799272442849",
# "979191338754992",
# "559357297257163626956188267"]
Now, for each element of shorter_digit_arr, find the 13-character substring whose product of digits is greatest, then find the largest of those (shorter_digit_arr.size #=> 24) products. The main benefit of splitting the string into substrings in this way is that absence of zeroes allows us to perform the product calculations in a more efficient way than simply grinding out 12 multiplications for each substring:
res = shorter_digit_arr.map do |s|
cand = s[0,13].each_char.reduce(1) { |prod,t| prod * t.o_i }
best = { val: cand, offset: 0 }
(13...s.size).each do |i|
cand = cand*(s[i].to_i)/(s[i-13].to_i)
best = { val: cand, offset: i-12 } if cand > best[:val]
end
[best[:val], s[best[:offset],13]]
end.max_by(&:first)
#=> [23514624000, "5576689664895"]
puts "max_product: %d for: '%s'" % res
#=> max_product: 23514624000 for: '5576689664895'
The solution is the last 13 characters of:
s = shorter_digit_arr[7]
#=> "435576689664895"
The key here is the line:
cand = cand*(s[i].to_i)/(s[i-13].to_i)
which computes a 13-digit product by multiplying the "previous" 13-digit product by the digit added and dividing it by the digit dropped off.
In finding the maximum product for this element, the calculations are as follows:
s = "435576689664895"
cand = s[0,13].each_char.reduce(1) { |prod,t| prod * t.to_i }
#=> = "4355766896648".each_char.reduce(1) { |prod,t| prod * t.to_i }
# = 6270566400
best_val = { val: 6270566400, offset: 0 }
enum = (13...s.size).each
#=> #<Enumerator: 13...15:each>
The elements of this enumerator will be passed to the block by Enumerator#each. We can see what they are by converting enum to an array:
enum.to_a
#=> [13, 14]
We can use Enumerator#next to simulate the passing of the elements of enum to the block and their assignment to the block variable i.
Pass the first element of the enumerator (13) to the block:
i = enum.next
#=> 13
cand = cand*(s[i].to_i)/(s[i-13].to_i)
# = 6270566400*(s[13].to_i)/(s[0].to_i)
# = 6270566400*(9)/(4)
# = 14108774400
cand > best[:val]
#=> 14108774400 > 6270566400 => true
best = { val: cand, offset: i-12 }
#=> { val: 14108774400, offset: 1 }
Pass the second element (14) to the block:
i = enum.next
#=> 14
cand = cand*(s[i].to_i)/(s[i-13].to_i)
#=> = 14108774400*(s[14].to_i)/(s[1].to_i)
# = 14108774400*(5)/(3)
# = 23514624000
cand > best[:val]
#=> 23514624000 > 14108774400 => true
best = { val: 23514624000, offset: 2 }
All elements of the enumerator have now been passed to the block. We can confirm that:
i = enum.next
#=> StopIteration: iteration reached an end
The result (for shorter_digit_arr[7]) is:
[best[:val], s[best[:offset],13]]
#=> [23514624000, "435576689664895"[2,13]]
# [23514624000, "5576689664895"]

How do I use a hash to modify the values of an Array?

I am building a base converter. Here is my code so far:
def num_to_s(num, base)
remainders = [num]
while base <= num
num /= base #divide initial value of num
remainders << num #shovel results into array to map over for remainders
end
return remainders.map{|i| result = i % base}.reverse.to_s #map for remainders and shovel to new array
puts num_to_s(40002, 16)
end
Now it's time to account for bases over 10 where letters replace numbers. The instructions (of the exercise) suggest using a hash. Here is my hash:
conversion = {10 => 'A', 11 => 'B', 12 => 'C', 13 => 'D', 14 => 'E', 15 => 'F',}
The problem is now, how do I incorporate it so that it modifies the array? I have tried:
return remainders.map{|i| result = i % base}.map{|i| [i, i]}.flatten.merge(conversion).reverse.to_s
In an attempt to convert the 'remainders' array into a hash and merge them so the values in 'conversion' override the ones in 'remainders', but I get an 'odd list for Hash' error. After some research it seems to be due to the version of Ruby (1.8.7) I am running, and was unable to update. I also tried converting the array into a hash outside of the return:
Hashes = Hash[remainders.each {|i, i| [i, i]}].merge(conversion)
and I get an 'dynamic constant assignment' error. I have tried a bunch of different ways to do this... Can a hash even be used to modify an array? I was also thinking maybe I could accomplish this by using a conditional statement within an enumerator (each? map?) but haven't been able to make that work. CAN one put a conditional inside an enumerator?
Yes, you could use a hash:
def digit_hash(base)
digit = {}
(0...[10,base].min).each { |i| digit.update({ i=>i.to_s }) }
if base > 10
s = ('A'.ord-1).chr
(10...base).each { |i| digit.update({ i=>s=s.next }) }
end
digit
end
digit_hash(40)
#=> { 0=>"0", 1=>"1", 2=>"2", 3=>"3", 4=>"4",
# 5=>"5", 6=>"6", 7=>"7", 8=>"8", 9=>"9",
# 10=>"A", 11=>"B", 12=>"C", ..., 34=>"Y", 35=>"Z",
# 36=>"AA", 37=>"AB", 38=>"AC", 39=>"AD" }
There is a problem in displaying digits after 'Z'. Suppose, for example, the base were 65. Then one would not know if "ABC" was 10-11-12, 37-12 or 10-64. That's detail we needn't worry about.
For variety, I've done the base conversion from high to low, as one might do with paper and pencil for base 10:
def num_to_s(num, base)
digit = digit_hash(base)
str = ''
fac = base**((0..Float::INFINITY).find { |i| base**i > num } - 1)
until fac.zero?
d = num/fac
str << digit[d]
num -= d*fac
fac /= base
end
str
end
Let's try it:
num_to_s(134562,10) #=> "134562"
num_to_s(134562, 2) #=> "100000110110100010"
num_to_s(134562, 8) #=> "406642"
num_to_s(134562,16) #=> "20DA2"
num_to_s(134562,36) #=> "2VTU"
Let's check the last one:
digit_inv = digit_hash(36).invert
digit_inv["2"] #=> 2
digit_inv["V"] #=> 31
digit_inv["T"] #=> 29
digit_inv["U"] #=> 30
So
36*36*36*digit_inv["2"] + 36*36*digit_inv["V"] +
36*digit_inv["T"] + digit_inv["U"]
#=> 36*36*36*2 + 36*36*31 + 36*29 + 30
#=> 134562
The expression:
(0..Float::INFINITY).find { |i| base**i > num }
computes the smallest integer i such that base**i > num. Suppose, for example,
base = 10
num = 12345
then i is found to equal 5 (10**5 = 100_000). We then raise base to this number less one to get the initial factor:
fac = base**(5-1) #=> 10000
Then the first (base-10) digit is
d = num/fac #=> 1
the remainder is
num -= d*fac #=> 12345 - 1*10000 => 2345
and the factor for the next digit is:
fac /= base #=> 10000/10 => 1000
I made a couple of changes from my initial answer to make it 1.87-friedly (I removed Enumerator#with_object and Integer#times), but I haven't tested with 1.8.7, as I don't have that version installed. Let me know if there are any problems.
Apart from question, you can use Fixnum#to_s(base) to convert base.
255.to_s(16) # 'ff'
I would do a
def get_symbol_in_base(blah)
if blah < 10
return blah
else
return (blah - 10 + 65).chr
end
end
and after that do something like:
remainders << get_symbol_in_base(num)
return remainders.reverse.to_s

How can I convert a human-readable number to a computer-readable number in Ruby?

I'm working in Ruby with an array that contains a series of numbers in human-readable format (e.g., 2.5B, 1.27M, 600,000, where "B" stands for billion, "M" stands for million). I'm trying to convert all elements of the array to the same format.
Here is the code I've written:
array.each do |elem|
if elem.include? 'B'
elem.slice! "B"
elem = elem.to_f
elem = (elem * 1000000000)
else if elem.include? 'M'
elem.slice! "M"
elem = elem.to_f
elem = (elem * 1000000)
end
end
When I inspect the elements of the array using puts(array), however, the numbers appear with the "B" and "M" sliced off but the multiplication conversion does not appear to have been applied (e.g., the numbers now read 2.5, 1.27, 600,000, instead of 2500000000, 1270000, 600,000).
What am I doing wrong?
First thing to note is that else if in ruby is elsif. See http://www.tutorialspoint.com/ruby/ruby_if_else.htm
Here is a working function for you to try out:
def convert_array_items_from_human_to_integers(array)
array.each_with_index do |elem,i|
if elem.include? 'B'
elem.slice! "B"
elem = elem.to_f
elem = (elem * 1000000000)
elsif elem.include? 'M'
elem.slice! "M"
elem = elem.to_f
elem = (elem * 1000000)
end
array[i] = elem
end
return array
end
Calling convert_array_items_from_human_to_integers(["2.5B", "1.2M"])
returns [2500000000.0, 1200000.0]
Another variation:
array = ['2.5B', '1.27M', '$600000']
p array.each_with_object([]) { |i, a|
i = i.gsub('$', '')
a << if i.include? 'B'
i.to_f * 1E9
elsif i.include? 'M'
i.to_f * 1E6
else
i.to_f
end
}
#=> [2500000000.0, 1270000.0, 600000.0]
Try this:
array.map do |elem|
elem = elem.gsub('$','')
if elem.include? 'B'
elem.to_f * 1000000000
elsif elem.include? 'M'
elem.to_f * 1000000
else
elem.to_f
end
end
This uses map instead of each to return a new array. Your attempt assigns copies of the array elements, leaving the original array in place (except for the slice!, which modifies in place). You can dispense with the slicing in the first place, since to_f will simply ignore any non-numeric characters.
EDIT:
If you have leading characters such as $2.5B, as your question title indicates (but not your example), you'll need to strip those explicitly. But your sample code doesn't handle those either, so I assume that's not an issue.
Expanding a bit on pjs' answer:
array.each do |elem|
elem is a local variable pointing to each array element, one at a time. When you do this:
elem.slice! "B"
you are sending a message to that array element telling it to slice the B. And you're seeing that in the end result. But when you do this:
elem = elem.to_f
now you've reassigned your local variable elem to something completely new. You haven't reassigned what's in the array, just what elem is.
Here's how I'd go about it:
ARY = %w[2.5B 1.27M 600,000]
def clean_number(s)
s.gsub(/[^\d.]+/, '')
end
ARY.map{ |v|
case v
when /b$/i
clean_number(v).to_f * 1_000_000_000
when /m$/i
clean_number(v).to_f * 1_000_000
else
clean_number(v).to_f
end
}
# => [2500000000.0, 1270000.0, 600000.0]
The guts of the code are in the case statement. A simple check for the multiplier allows me to strip the undesired characters and multiply by the right value.
Normally we could use to_f to find the floating-point number to be multiplied for strings like '1.2', but it breaks down for things like '$1.2M' because of the "$". The same thing is true for embedded commas marking thousands:
'$1.2M'.to_f # => 0.0
'1.2M'.to_f # => 1.2
'6,000'.to_f # => 6.0
'6000'.to_f # => 6000.0
To fix the problem for simple strings containing just the value, it's not necessary to do anything fancier than stripping undesirable characters using gsub(/[^\d.]+/, ''):
'$1.2M'.gsub(/[^\d.]+/, '') # => "1.2"
'1.2M'.gsub(/[^\d.]+/, '') # => "1.2"
'6,000'.gsub(/[^\d.]+/, '') # => "6000"
'6000'.gsub(/[^\d.]+/, '') # => "6000"
[^\d.] means "anything NOT a digit or '.'.
Be careful how you convert your decimal values to integers. You could end up throwing away important precision:
'0.2M'.gsub(/[^\d.]+/, '').to_f * 1_000_000 # => 200000.0
'0.2M'.gsub(/[^\d.]+/, '').to_i * 1_000_000 # => 0
('0.2M'.gsub(/[^\d.]+/, '').to_f * 1_000_000).to_i # => 200000
Of course all this breaks down if your string is more complex than a simple number and multiplier. It's easy to break down a string and identify those sort of sub-strings, but that's a different question.
I would do it like this:
Code
T, M, B = 1_000, 1_000_000, 1_000_000_000
def convert(arr)
arr.map do |n|
m = n.gsub(/[^\d.TMB]/,'')
m.to_f * (m[-1][/[TMB]/] ? Object.const_get(m[-1]) : 1)
end
end
Example
arr = %w[$2.5B 1.27M 22.5T, 600,000]
convert(arr)
# => [2500000000.0, 1270000.0, 22500.0, 600000.0]
Explanation
The line
m = n.gsub(/[^\d.TMB]/,'')
# => ["2.5B", "1.27M", "22.5T", "600000"]
merely eliminates unwanted characters.
m.to_f * (m[-1][/[TMB]/] ? Object.const_get(m[-1]) : 1)
returns the product of the string converted to a float and a constant given by the last character of the string, if that character is T, M or B, else 1.
Actual implementation might be like this:
class A
T, M, B = 1_000, 1_000_000, 1_000_000_000
def doit(arr)
c = self.class.constants.map(&:to_s).join
arr.map do |n|
m = n.gsub(/[^\d.#{c}]/,'')
m.to_f * (m[-1][/[#{c}]/] ? self.class.const_get(m[-1]) : 1)
end
end
end
If we wished to change the reference for 1,000 from T to K and add T for trillion, we would need only change
T, M, B = 1_000, 1_000_000, 1_000_000_000
to
K, M, B, T = 1_000, 1_000_000, 1_000_000_000, 1_000_000_000_000

Resources