Ruby implementation is_numeric? for Strings, need better alternatives
Question
I wanted to validate 'numericality' of a string (its not an attribute in an active-record model). I just need it to be a valid base 10, positive integer string. I am doing this:
class String
def numeric?
# Check if every character is a digit
!!self.match(/\A[0-9]+\Z/)
end
end
class String
def numeric?
# Check is there is *any* non-numeric character
!self.match(/[^0-9]/)
end
end
Which of these is a more plausible alternative? OR, is there any other better implementation?
Solution
Please make sure use \A
and \Z
rather than ^
and $
, to match the entire string rather than just a single line in the string. If you want to avoid matching a string with an ending newline, use '\z' at the end. For more issues, see The Regex Tutorial on anchors.
For example, /^[0-9]+$/
successfully matches the following:
foo
1234
bar
but /\A[0-9]+\Z/
does not.
OTHER TIPS
The first one looks sane to me.
I'd name the method numeric?
, though. I'm not a big fan of is_foo?
methods. They make sense in languages that doesn't have question marks in method names (is_foo
, isFoo
), but with the question mark, the is
feels redundant.
I'm not a 100% certain but Rails seems to be using /\A[+-]?\d+\Z/
for integers.
Click on show source for validates_numericality_of
here
I'd suggest another way of doing it. Also, because you asked "positive" integer, I made two separate methods for positive integer and non-negative integer.
class String
def numeric?
!self.match(/[^0-9]/)
end
def positive_integer?
self.to_i > 0
end
def nonnegative_integer?
self.to_i > 0 or self == '0'
end
end
Here's the benchmark code:
require 'benchmark'
include Benchmark
bmbm(100) do |x|
x.report('numeric?') do
"some invalid string".numeric?
end
x.report('positive_integer?') do
"some invalid string".positive_integer?
end
x.report('nonnegative_integer?') do
"some invalid string".nonnegative_integer?
end
end
Result:
numeric?
0.000000 0.000000 0.000000 ( 0.000045)
positive_integer?
0.000000 0.000000 0.000000 ( 0.000012)
nonnegative_integer?
0.000000 0.000000 0.000000 ( 0.000015)
It seems like positive_integer?
and nonnegative_integer?
are faster in this micro-benchmark.
Finally, as a side note, you can define integer?
method in a similar fashion:
class String
def integer?
self.to_i.to_s == self
end
end
The second will finish quicker in the case of a non-numeric string, as it will reject on the first bad character.
Also, check out the String#to_i method - it possibly does what you want:
http://www.ruby-doc.org/core/classes/String.html#M000787
I dont know if this is fast, but I like:
class String
def numeric?
true if Integer(object) rescue false
end
end
Handles negative numbers as well. And if you ever wanted to support floats in the future, just use Float()
According to a simple benchmark, the second approach is faster, although I'm not expert benchmarker, so this might not be a valid benchmark: http://pastie.org/586777
Zalus' logic is right. It only needs to check once for a non-valid string.
Notice
n = '1234'
n.to_i.to_s == n
=> true
n2 = '1.3'
n.to_i.to_s == n2
=> false
works for positive & negative integers, but not octal/hex representations, floats etc. May not perform the best (untested), but no point wasting time with premature optimizations.