158

I need a function, is_an_integer, where

  • "12".is_an_integer? returns true.
  • "blah".is_an_integer? returns false.

How can I do this in Ruby? I would write a regex but I'm assuming there is a helper for this that I am not aware of.

3
  • 1
    possible duplicate of Test if string is a number in Ruby on Rails Commented Sep 17, 2013 at 11:01
  • 2
    Be careful using solutions relying on regular expressions. Benchmarks show that they run much more slowly than regular code. Commented Jul 14, 2019 at 23:11
  • There is no canonical way to turn a ruby string integer into an integer? Commented Jan 15, 2024 at 9:32

19 Answers 19

208

Well, here's the easy way:

class String
  def is_integer?
    self.to_i.to_s == self
  end
end

>> "12".is_integer?
=> true
>> "blah".is_integer?
=> false

I don't agree with the solutions that provoke an exception to convert the string - exceptions are not control flow, and you might as well do it the right way. That said, my solution above doesn't deal with non-base-10 integers. So here's the way to do with without resorting to exceptions:

  class String
    def integer? 
      [                          # In descending order of likeliness:
        /^[-+]?[1-9]([0-9]*)?$/, # decimal
        /^0[0-7]+$/,             # octal
        /^0x[0-9A-Fa-f]+$/,      # hexadecimal
        /^0b[01]+$/              # binary
      ].each do |match_pattern|
        return true if self =~ match_pattern
      end
      return false
    end
  end
Sign up to request clarification or add additional context in comments.

15 Comments

Couldn't you replace self.to_i.to_s == self with Integer self rescue false ?
You could, but that would be bad form. You don't use exceptions as control flow, and no one's code should ever contain "rescue false" (or "rescue true"). Some simple gsub'ing would make my solution work for edge cases not specified by the OP.
I know a lot of people use it, and it's certainly aesthetically pleasing. To me though it's an indication that the code needs restructuring. If you're expecting an exception...it's not an exception.
I agree that exceptions should not be used as control flow. I don't think that the requirement is that developer oriented numbers be recognized. In non-programmer situations that could be seen as a bug, especially given that possible confusion around leading zeros and octal. Also not consistent with to_i. Your code doesn't handle the "-0123" case. Once you do handle that case, you don't need a separate regexp for octal. You can simply further by using "any?". The only statement in your function could be "[ /re1/, /re2/, /re3/ ].any? { |re| self =~ re }", with no if clauses or returns.
I'm all in favor for this solution, except for one mostly harmless snag: '-0'.is_integer? == false
|
146

You can use regular expressions. Here is the function with @janm's suggestions.

class String
    def is_i?
       !!(self =~ /\A[-+]?[0-9]+\z/)
    end
end

An edited version according to comment from @wich:

class String
    def is_i?
       /\A[-+]?\d+\z/ === self
    end
end

In case you only need to check positive numbers

  if !/\A\d+\z/.match(string_to_check)
      #Is not a positive number
  else
      #Is all good ..continue
  end  

10 Comments

Not bad. In Ruby you usually omit the "return" keyword if the return value is generated in the last expression in the function. This will also return an integer value of zero, you probably want a boolean, so something like !!(str =~ /^[-+]?[0-9]+$/) would do that. Then you could add it to String and leave out the argument, using "self" instead of "str", and then you could change the name to "is_i?" ...
Thanks! I have absolutely no clue about ruby conventions and practices. I just did a quick google on ruby and regular expressions to see the syntax, changed the regex to apply to the problem at hand, and tested it. It's pretty neat actually .. I may have to give it a longer look when I have more spare time.
Sarah, that's not a bug, that's a feature!
Two comments. You can use /regexp/ === self instead of the !!(self =~ /regexp/) construct. You can use character class '\d' instead of [0-9]
The simplest regex for an integer probably is /^\d+$/
|
73

You can use Integer(str) and see if it raises:

def is_num?(str)
  !!Integer(str)
rescue ArgumentError, TypeError
  false
end

It should be pointed out that while this does return true for "01", it does not for "09", simply because 09 would not be a valid integer literal. If that's not the behaviour you want, you can add 10 as a second argument to Integer, so the number is always interpreted as base 10.

19 Comments

Dude...provoking an exception just to convert a number? Exceptions are not control flow.
They aren't, but unfortunately this is the canonical way to determine "integerness" of a string in Ruby. Methods using #to_i are just too broken because of it's permissiveness.
For those wondering why, Integer("09") is not valid because the "0" makes it octal, and 9 is not a valid octal number. osdir.com/ml/lang.ruby.general/2002-08/msg00247.html
Sarah: you can use a Regex but in order to handle all the cases that Ruby does when parsing integers (negative numbers, hex, octal, underscores e.g. 1_000_000) it would be a very big Regex and easy to get wrong. Integer() is canonical because with Integer ()you know for sure that anything that Ruby considers an integer literal will be accepted, and everything else will be rejected. Duplicating what the language already gives you is arguably a worse code smell than using exceptions for control.
@Rado So is reinventing the wheel.
|
58

Ruby 2.6.0 enables casting to an integer without raising an exception, and will return nil if the cast fails. And since nil mostly behaves like false in Ruby, you can easily check for an integer like so:

if Integer(my_var, exception: false)
  # do something if my_var can be cast to an integer
end

2 Comments

This is the cleanest of all!
If you need some kind of strict type validation, this is the way to go, as "2hey".to_i == 2 returns true, but Integer("2hey", exception: false) returns nil
27

You can do a one liner:

str = ...
int = Integer(str) rescue nil

if int
  int.times {|i| p i}
end

or even

int = Integer(str) rescue false

Depending on what you are trying to do you can also directly use a begin end block with rescue clause:

begin
  str = ...
  i = Integer(str)

  i.times do |j|
    puts j
  end
rescue ArgumentError
  puts "Not an int, doing something else"
end

1 Comment

With regard to the topic "exception as control flow": since we do not know how the method at hand is to be used we cannot really judge whether exceptions would fit or not. If the string is input and it is required to be an integer, then providing a non integer would warrant an exception. Although then maybe the handling is not in the same method and we would probably just do Integer(str).times {|i| puts i} or whatever.
26
"12".match(/^(\d)+$/)      # true
"1.2".match(/^(\d)+$/)     # false
"dfs2".match(/^(\d)+$/)    # false
"13422".match(/^(\d)+$/)   # true

4 Comments

It doesn't return true and false but MatchData instances and nil
It is not what it returns, but if it matches
Wrap it with !! or use present? if you need a boolean !!( "12".match /^(\d)+$/ ) or "12".match(/^(\d)+$/).present? (the latter requiring Rails/activesupport)
This regular expression does not take a sign into account: negative numbers are valid integer numbers too. You're now testing for valid natural numbers or zero.
8
class String
  def integer?
    Integer(self)
    return true
  rescue ArgumentError
    return false
  end
end
  1. It isn't prefixed with is_. I find that silly on questionmark methods, I like "04".integer? a lot better than "foo".is_integer?.
  2. It uses the sensible solution by sepp2k, which passes for "01" and such.
  3. Object oriented, yay.

5 Comments

+1 for naming it #integer?, -1 for cluttering up String with it :-P
Where else would it go? integer?("a string") ftl.
String#integer? is the kind of common patch that every Ruby coder and their cousin likes to add to the language, leading to codebases with three different subtly incompatible implementations and unexpected breakage. I learned this the hard way on large Ruby projects.
Same comment as above: exceptions shouldn't be used for control flow.
Downside: this solution is wasting one conversion.
8

The Best and Simple way is using Float

val = Float "234" rescue nil

Float "234" rescue nil #=> 234.0

Float "abc" rescue nil #=> nil

Float "234abc" rescue nil #=> nil

Float nil rescue nil #=> nil

Float "" rescue nil #=> nil

Integer is also good but it will return 0 for Integer nil

3 Comments

I'm glad I noticed your answer. Otherwise I would have gone with "Integer" when I needed "Float".
This is simple but the best answer! We don't really need fancy patch to String class in most of the cases. This works best for me!
(Float(value) rescue false) ? Float(value).to_s==value ? Float(value) : Integer(value) : value
7

I prefer:

config/initializers/string.rb

class String
  def number?
    Integer(self).is_a?(Integer)
  rescue ArgumentError, TypeError
    false
  end
end

and then:

[218] pry(main)> "123123123".number?
=> true
[220] pry(main)> "123 123 123".gsub(/ /, '').number?
=> true
[222] pry(main)> "123 123 123".number?
=> false

or check phone number:

"+34 123 456 789 2".gsub(/ /, '').number?

Comments

6

A much simpler way could be

/(\D+)/.match('1221').nil? #=> true
/(\D+)/.match('1a221').nil? #=> false
/(\D+)/.match('01221').nil? #=> true

Comments

5

Personally I like the exception approach although I would make it a little more terse:

class String
  def integer?(str)
    !!Integer(str) rescue false
  end
end

However, as others have already stated, this doesn't work with Octal strings.

Comments

3
  def isint(str)
    return !!(str =~ /^[-+]?[1-9]([0-9]*)?$/)
  end

1 Comment

Code only answers are not very useful. Instead provide an explanation of how it works and why it's the appropriate answer. We want to educate for the future so the solution is understood, not solve the immediate question.
3

Ruby 2.4 has Regexp#match?: (with a ?)

def integer?(str)
  /\A[+-]?\d+\z/.match? str
end

For older Ruby versions, there's Regexp#===. And although direct use of the case equality operator should generally be avoided, it looks very clean here:

def integer?(str)
  /\A[+-]?\d+\z/ === str
end

integer? "123"    # true
integer? "-123"   # true
integer? "+123"   # true

integer? "a123"   # false
integer? "123b"   # false
integer? "1\n2"   # false

Comments

3

This might not be suitable for all cases simplely using:

"12".to_i   => 12
"blah".to_i => 0

might also do for some.

If it's a number and not 0 it will return a number. If it returns 0 it's either a string or 0.

1 Comment

Works but it's not recommended, since "12blah".to_i => 12. This might cause some trouble in weird scenarios.
3

Here's my solution:

# /initializers/string.rb
class String
  IntegerRegex = /^(\d)+$/

  def integer?
    !!self.match(IntegerRegex)
  end
end

# any_model_or_controller.rb
'12345'.integer? # true
'asd34'.integer? # false

And here's how it works:

  • /^(\d)+$/is regex expression for finding digits in any string. You can test your regex expressions and results at http://rubular.com/.
  • We save it in a constant IntegerRegex to avoid unnecessary memory allocation everytime we use it in the method.
  • integer? is an interrogative method which should return true or false.
  • match is a method on string which matches the occurrences as per the given regex expression in argument and return the matched values or nil.
  • !! converts the result of match method into equivalent boolean.
  • And declaring the method in existing String class is monkey patching, which doesn't change anything in existing String functionalities, but just adds another method named integer? on any String object.

2 Comments

Could you add a little explanation to this please?
@stef - I've done the same. Please let me know if you have any query still.
1

Expanding on @rado's answer above one could also use a ternary statement to force the return of true or false booleans without the use of double bangs. Granted, the double logical negation version is more terse, but probably harder to read for newcomers (like me).

class String
  def is_i?
     self =~ /\A[-+]?[0-9]+\z/ ? true : false
  end
end

1 Comment

Consider that using regular expressions forces Ruby to do a lot more work so if this is used in a loop it'll slow the code. Anchoring the expression helps, but regex are still significantly slower.
1

For more generalised cases (including numbers with decimal point), you can try the following method:

def number?(obj)
  obj = obj.to_s unless obj.is_a? String
  /\A[+-]?\d+(\.[\d]+)?\z/.match(obj)
end

You can test this method in an irb session:

(irb)
>> number?(7)
=> #<MatchData "7" 1:nil>
>> !!number?(7)
=> true
>> number?(-Math::PI)
=> #<MatchData "-3.141592653589793" 1:".141592653589793">
>> !!number?(-Math::PI)
=> true
>> number?('hello world')
=> nil
>> !!number?('hello world')
=> false

For a detailed explanation of the regex involved here, check out this blog article :)

1 Comment

Isn't necessary to call obj.is_a? String because String#to_s will return itself, which I guess doesn't require too much processing compared with the .is_a? call. This way, you'll be making only one call in this line instead of one or two. Also, you could include directly !! inside the number? method, because by convention, a method name that ends with ? is supposed to return a boolean value. Regards!
-3

One liner in string.rb

def is_integer?; true if Integer(self) rescue false end

Comments

-3

I'm not sure if this was around when this question is asked but for anyone that stumbles across this post, the simplest way is:

var = "12"
var.is_a?(Integer) # returns false
var.is_a?(String) # returns true

var = 12
var.is_a?(Integer) # returns true
var.is_a?(String) # returns false

.is_a? will work with any object.

1 Comment

That's not what the original question is asking. OP wants to know if the string is an integer also. e.g. "12".is_an_integer? == true "not12".is_an_integer? == false 12.is_an_integer? == true

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.