Strictly convert string to integer (or nil)

RubyType Conversion

Ruby Problem Overview


For web programming, numbers come in as strings. but to_i will convert "5abc" to 5 and "abc" to 0, both wrong answers. To catch these, I wrote:

def number_or_nil( s )
  number = s.to_i
  number = nil if (number.to_s != s)
  return number
end

Is there a neater, more Ruby-natural way of accomplishing this conversion and detecting that the string wasn't intended as a number?

Ruby Solutions


Solution 1 - Ruby

Use Integer(string)

It will raise an ArgumentError error if the string cannot convert to an integer.

Integer('5abc') #=> ArgumentError: invalid value for Integer(): "5abc"
Integer('5') #=> 5

You'd still need your number_or_nil method if you want the behavior to be that nil is returned when a string cannot be converted.

def number_or_nil(string)
  Integer(string || '')
rescue ArgumentError
  nil
end

You should be careful to rescue from a particular exception. A bare rescue (such as "rescue nil") will rescue from any error which inherits from StandardError and may interfere with the execution of your program in ways you don't expect. Integer() will raise an ArgumentError, so specify that.

If you'd rather not deal with exceptions and just prefer a shorter version of your number_or_nil you can take advantage of implicit return values and write it as:

def number_or_nil(string)
  num = string.to_i
  num if num.to_s == string
end

number_or_nil '5' #=> 5
number_or_nil '5abc' #=> nil

This will work the way you expect.

Solution 2 - Ruby

Since at least Ruby 2.6, the kernel functions Integer, Float, etc. accept an exception keyword argument that does the job:

> Integer('42', exception: false)
=> 42
> Integer('x42', exception: false)
=> nil
> Integer('x42')
ArgumentError (invalid value for Integer(): "x42")
> Integer('', exception: false)
=> nil
> Integer('')
ArgumentError (invalid value for Integer(): "")
> Integer(nil, exception: false)
=> nil
> Integer(' 42 ', exception: false)
=> 42
> Integer(' 4 2 ', exception: false)
=> nil

Note that Integer also leaves you the control of the base, something that to_i does not support:

> '0xf'.to_i
=> 0
> Integer('0xf')
=> 15
> Integer('10', 8)
=> 8

When the base is specified, the radix-prefix (0x... etc.) must be consistent (if present):

> Integer('0xf', 10)
=> ArgumentError(invalid value for Integer(): "0xf")
> Integer('0xf', 16)
=> 15
> Integer('f', 16)
=> 15

Solution 3 - Ruby

Use a simple regex to check str is an integer.

def number_or_nil(str)
   str.to_i if str[/^-?\d+$/] and str.line.size == 1
end

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlistair CockburnView Question on Stackoverflow
Solution 1 - RubyJim GayView Answer on Stackoverflow
Solution 2 - RubyakimView Answer on Stackoverflow
Solution 3 - RubyhahchoView Answer on Stackoverflow