153

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.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Tony
  • 18,776
  • 31
  • 129
  • 193
  • 1
    possible duplicate of [Test if string is a number in Ruby on Rails](http://stackoverflow.com/questions/5661466/test-if-string-is-a-number-in-ruby-on-rails) – Jakob S Sep 17 '13 at 11:01
  • 2
    Be careful using solutions relying on regular expressions. Benchmarks show that they run much more slowly than regular code. – the Tin Man Jul 14 '19 at 23:11

19 Answers19

201

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
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Sarah Mei
  • 18,154
  • 5
  • 45
  • 45
  • 2
    Couldn't you replace `self.to_i.to_s == self` with `Integer self rescue false` ? – Meredith L. Patterson Aug 05 '09 at 21:38
  • 8
    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. – Sarah Mei Aug 05 '09 at 22:06
  • 1
    For the record, I think inline "rescue nil" is pretty useful, at least if used with discretion. – glenn mcdonald Aug 06 '09 at 01:37
  • 4
    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. – Sarah Mei Aug 06 '09 at 03:59
  • 2
    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. – janm Aug 06 '09 at 07:49
  • I liked this solution until dealing with commas and currency. `'$1,000'.to_i.to_s == '$1,000'` fails. – scarver2 Apr 19 '13 at 17:41
  • might want true when there's a space, e.g. `" 1"` – Ari Jul 19 '13 at 02:37
  • 3
    I'm all in favor for this solution, except for one mostly harmless snag: `'-0'.is_integer? == false` – thomax Oct 14 '13 at 11:50
  • Thanks for this solution – I've been using it a lot. Dealing with Rails where my model IDs might also be an actual Integer, I've added a clause to return true if `value.is_a? Integer`. – slothbear Dec 10 '13 at 19:03
  • I like your simple solution, a better name for it might be "is_id" - if you are checking for conversion to an id, you shouldn't have commas, whitespace, negative numbers or any of that. If you want to allow for leading zeros, a simpler regex can be tacked on: `self.to_i.to_s == self.sub(/^0+/,"")` – marksiemers May 16 '14 at 16:47
  • It's a waste of cycles to iterate on several regexes. Rather, put them all in parens, join them with `|`, and run it once. – akim Nov 15 '18 at 08:24
  • 1
    I think this solution adds complexity for the sake of strict adherence to a rule of thumb. In the case of using `Integer` for answering the question of whether a string contains a valid number, it's localized, robust, and easy to reason about code. The regex solution is harder to reason about and has greater potential for edge cases that need to be handled, in some cases complicating it further. The accepted answer had one such issue for several years, and this answer still has that same issue at the time of this writing. It matches strings such as "abc\n123\ndef". – lobati Sep 12 '19 at 03:13
  • `'1.0'.to_i.to_s != '1.0'` – Jon Schneider Sep 12 '19 at 15:04
  • '01'.to_i.to_s != '01' – felix Dec 14 '21 at 17:19
  • `'+2'.is_integer?` is `false` – Rorrim Jan 12 '23 at 16:12
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  
sscirrus
  • 55,407
  • 41
  • 135
  • 228
Rado
  • 8,634
  • 7
  • 31
  • 44
  • 4
    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?" ... – janm Aug 05 '09 at 22:16
  • 2
    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. – Rado Aug 05 '09 at 22:21
  • You've got the right idea, but it doesn't match binary or hex literals (see my edited solution below). – Sarah Mei Aug 06 '09 at 03:56
  • Sarah, that's not a bug, that's a feature! – janm Aug 06 '09 at 07:51
  • Well, given that I got two downvotes because my original solution didn't work on non-base-10 literals, I thought I'd spare him the same experience. ;) – Sarah Mei Aug 11 '09 at 17:44
  • 16
    Two comments. You can use `/regexp/ === self` instead of the `!!(self =~ /regexp/)` construct. You can use character class '\d' instead of `[0-9]` – wich Sep 24 '12 at 01:24
  • You should use \A \Z instead of ^ $. EXAMPLE: "\n9" should not be valid! – cintrzyk Oct 26 '13 at 20:37
  • @cintrzyk: Right, thanks. Used \z instead of \Z to match the end of the string as \Z disregards a trailing new line char. – Rado May 20 '14 at 20:05
  • This is quite nice answer. Just an observation, @cintrzyk noticed a place for update 4 years after the answer has been made. Now it is updated, but all other people who used the previous answer might have used not so perfect algorithm :) – Aleks May 27 '14 at 14:16
  • 1
    The simplest regex for an integer probably is /^\d+$/ – keithxm23 Jul 30 '14 at 20:28
72

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.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • 50
    Dude...provoking an exception just to convert a number? Exceptions are not control flow. – Sarah Mei Aug 05 '09 at 22:05
  • 32
    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. – Avdi Aug 05 '09 at 22:17
  • 18
    For those wondering why, Integer("09") is not valid because the "0" makes it octal, and 9 is not a valid octal number. http://osdir.com/ml/lang.ruby.general/2002-08/msg00247.html – Andrew Grimm Aug 05 '09 at 23:25
  • Avdi - if to_i won't work, why not do it with regexes? Provoking an exception may be canonical (though I couldn't find a reference) but it's still a smell. I edited my answer to include a regex solution. – Sarah Mei Aug 06 '09 at 04:08
  • 22
    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. – Avdi Aug 06 '09 at 14:32
  • If you remove the "else" clause above you will actually get the numeric value back. I would concede that the method name should probably changed then. I still wonder what the test method is needed for. We do not have context information unfortunately. – Robert Klemme Aug 07 '09 at 09:46
  • 3
    Also beware that using `Integer(str)` will convert strings like `0x26`, so a better method to use might be `Integer(str, 10)`. The 10 will only accept base 10 numbers. – Jason Axelson Nov 25 '13 at 18:38
  • 2
    @SarahMei: @sepp2k isn't using exceptions as control flow. It'd be that way if they did something like `raise ArgumentError` and later rescued from that exception. – Rafał Cieślak Apr 24 '14 at 14:19
  • 1
    -1 since you shouldn't use exception for control flow. It is considered an anti-pattern in computer science! – Rado May 20 '14 at 19:52
  • 10
    @Rado So is reinventing the wheel. – sepp2k May 20 '14 at 20:13
  • @sepp2k: +1 for funny, but maybe SO should consider which answers to reinvent the wheel with. I did a quick benchmark of my answer vs yours. Here are the results: Benchmark result: https://gist.github.com/anonymous/40c2f51e98ae41f9249e Source: https://gist.github.com/anonymous/12a8e22cffb4208deb21 – Rado May 20 '14 at 21:20
  • 4
    Huzzah for a working solution to a ridiculous problem. There should be a better way to do this within ruby. The need for regexp to solve this problem is far more absurd. Complaints about using exceptions as control flow are dogmatic nonsense, this is a working solution to a problem which (while ugly) works! If you have a bullet proof regexp solution, make a PR against the ruby core! – Chris Hanson Dec 15 '14 at 16:24
  • This is a hell of a lot better than trying to work through somebody's (possibly) buggy Regex. Regarding exceptions: compare this to other languages like C# that can throw FormatException when calling Int32.Parse(). Java does something similar. Look at how Python solves this problem (basically you'll see all kinds of long complex solutions that parse manually, use regex, or throw exceptions). – Matthew Kraus Sep 23 '16 at 21:55
  • 2
    @Rado regarding exception control flow "anti-pattern" in computer science--this isn't universally true. This isn't a Python question, but consider how exceptions can be used in Python: (1) error handling, (2) event notification, (3) special-case handling, (4) termination actions, (5) unusual control flows (Mark Lutz, "Learning Python", 5th ed, pg 1082-3). From Lut'z book, also check out the idiom for breaking out of multiple nested loops (using exceptions) on page 1145. And read how Exceptions Aren't Always Errors pg 1146. Ruby can do these things too. Duck typed languages aren't so dogmatic. – Matthew Kraus Sep 23 '16 at 22:05
  • Note that if `str` happens to be a Float value, like `1.23`, this will return true because Integer(1.23) will convert the number to integer. – psmith Sep 30 '16 at 02:57
  • @psmith Nope. It won't. pry(main)> Integer "1.25" rescue false => false – wrzasa Oct 17 '16 at 09:23
  • @wrzasa That's a string value. I was talking about this: `!!Integer(1.25) rescue false => true` (note the lack of quotes) – psmith Oct 19 '16 at 04:50
  • @psmith yes, this is a String, because this question is about strings... – wrzasa Oct 21 '16 at 17:38
  • @MatthewKraus Yes, it isn't universally true. They are considered anti-pattern for control-flow because they are usually more expensive. Now, are there times where they should be used? Sure - exceptional cases - hence the name. That however does not mean that they should be used all the time, especially for something like what the question asked. Check the benchmark I provided - it's evident from it that exceptions are really expensive in this case. The only time where this outperforms regex is when an exception is *not* thrown. This, in itself, tells the whole story. – Rado Nov 28 '16 at 21:10
46

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
Timitry
  • 2,646
  • 20
  • 26
27
"12".match(/^(\d)+$/)      # true
"1.2".match(/^(\d)+$/)     # false
"dfs2".match(/^(\d)+$/)    # false
"13422".match(/^(\d)+$/)   # true
25

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
Robert Klemme
  • 2,149
  • 18
  • 23
  • 1
    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. – Robert Klemme Aug 06 '09 at 07:07
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.
August Lilleaas
  • 54,010
  • 13
  • 102
  • 111
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

Siva
  • 7,780
  • 6
  • 47
  • 54
  • I'm glad I noticed your answer. Otherwise I would have gone with "Integer" when I needed "Float". – Jeff Zivkovic May 30 '18 at 03:21
  • 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! – Anh Nguyen Jun 28 '18 at 06:50
  • (Float(value) rescue false) ? Float(value).to_s==value ? Float(value) : Integer(value) : value – okliv Jan 25 '19 at 18:44
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?
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
skozz
  • 2,662
  • 3
  • 26
  • 37
5

A much simpler way could be

/(\D+)/.match('1221').nil? #=> true
/(\D+)/.match('1a221').nil? #=> false
/(\D+)/.match('01221').nil? #=> true
gouravtiwari21
  • 369
  • 3
  • 10
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.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
eightbitraptor
  • 267
  • 2
  • 6
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.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
three
  • 8,262
  • 3
  • 35
  • 39
  • 13
    Works but it's not recommended, since ```"12blah".to_i => 12```. This might cause some trouble in weird scenarios. – rfsbraz Jun 17 '15 at 11:24
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.
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Sachin
  • 963
  • 11
  • 31
3
  def isint(str)
    return !!(str =~ /^[-+]?[1-9]([0-9]*)?$/)
  end
Brian Webster
  • 30,033
  • 48
  • 152
  • 225
Amal Kumar S
  • 15,555
  • 19
  • 56
  • 88
  • 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. – the Tin Man Jul 14 '19 at 23:04
2

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
Stefan
  • 109,145
  • 14
  • 143
  • 218
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
adeluccar
  • 152
  • 1
  • 2
  • 12
  • 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. – the Tin Man Jul 14 '19 at 23:08
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 :)

tg_so
  • 496
  • 6
  • 11
  • Isn't necessary to call `obj.is_a? String` because [String#to_s](https://ruby-doc.org/core-2.2.0/String.html#method-i-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! – Giovanni Benussi Oct 05 '17 at 15:32
-3

One liner in string.rb

def is_integer?; true if Integer(self) rescue false end
Richard Erickson
  • 2,568
  • 8
  • 26
  • 39
cb24
  • 577
  • 5
  • 7
-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.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Newbie
  • 9
  • 1
    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` – Marklar Apr 25 '16 at 11:40