10

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?

3
  • Out of curiousity, why the {1,1} multiplier? By default all character classes and literals are matched exactly once unless otherwise specified. This is redundant. Commented Aug 17, 2009 at 9:15
  • Poor me! I'll remove it at once. Commented Aug 17, 2009 at 9:42
  • Related question: stackoverflow.com/questions/694176/… Commented Apr 28, 2011 at 3:26

8 Answers 8

10

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.

Sign up to request clarification or add additional context in comments.

Comments

5

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.

1 Comment

I ended up using the second one.
3

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

1 Comment

\A -> Start of string \Z -> End of string Basically the same I am unsing in First one, except the positive/negative modifier part.
2

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

Comments

1

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

1 Comment

the problem with to_i is you never know if it is 0 or not a number.
1

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()

2 Comments

Does this really work? Where does object come from? On my ruby 1.9.2-p180 it always returns false because object is undefined.
I think object should be self in this case.
0

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.

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.