Lefty
Lefty

Reputation: 695

Ruby .to_i does not return the complete integer as expected

My ruby command is,

"980,323,344.00".to_i

Why does it return 980 instead of 980323344?

Upvotes: 0

Views: 1287

Answers (3)

Stefan
Stefan

Reputation: 114158

The documentation for to_i might be a bit misleading:

Returns the result of interpreting leading characters in str as an integer base base (between 2 and 36)

"interpreting" doesn't mean that it tries to parse various number formats (like Date.parse does for date formats). It means that it looks for what's a valid integer literal in Ruby (in the given base). For example:

 1234.            #=> 1234
'1234'.to_i       #=> 1234

 1_234.           #=> 1234
'1_234'.to_i.     #=> 1234

 0d1234           #=> 1234
'0d1234'.to_i     #=> 1234

 0x04D2           #=> 1234
'0x04D2'.to_i(16) #=> 1234

Your input as a whole however is not a valid integer literal: (Ruby doesn't like the ,)

980,323,344.00
# SyntaxError (syntax error, unexpected ',', expecting end-of-input)
# 980,323,344.00
#    ^

But it starts with a valid integer literal. And that's where the the seconds sentence comes into play:

Extraneous characters past the end of a valid number are ignored.

So the result is 980 – the leading characters which form a valid integer converted to an integer.


If your strings always have that format, you can just delete the offending commas and run the result through to_i which will ignore the trailing .00:

'980,323,344.00'.delete(',')      #=> "980323344.00"
'980,323,344.00'.delete(',').to_i #=> 980323344

Otherwise you could use a regular expression to check its format before converting it:

input = '980,323,344.00'
number = case input
         when /\A\d{1,3}(,\d{3})*\.00\z/
           input.delete(',').to_i
         when /other format/
           # other conversion
         end

And if you are dealing with monetary values, you should consider using the money gem and its monetize addition for parsing formatted values:

 amount = Monetize.parse('980,323,344.00')
 #=> #<Money fractional:98032334400 currency:USD>

 amount.format
 #=> "$980.323.344,00"

Note that format requires i18n so the above example might require some setup.

Upvotes: 2

lacostenycoder
lacostenycoder

Reputation: 11216

In ruby calling to_i on a string will truncate from the beginning of a string where possible.

number_string = '980,323,344.00'
number_string.delete(',').to_i
#=> 980323344

"123abc".to_i
#=> 123

If you want to add underscores to make longer number more readable, those can be used where the conventional commas would be in written numbers.

"980_323_344.00".to_i
#=> 980323344

Upvotes: 3

bitsapien
bitsapien

Reputation: 1833

You can achieve it by doing this :

"980,323,344.00".delete(',').to_i

The reason your method call to to_i does not return as expected is explained here, and to quote, the method :

Returns the result of interpreting leading characters in str as an integer base base (between 2 and 36). Extraneous characters past the end of a valid number are ignored.

Extraneous characters in your case would be the comma character that ends at 980, the reason why you see 980 being returned

Upvotes: 7

Related Questions