String is a number?
Anton Jenkins | June 22, 2009
I’ve been fishing around to see if Ruby has any way of telling whether a String object contains a valid number. The is_a? method looked like it might be a winner for a little while…
1 2 3 4 5 |
>> 34.is_a?(Numeric) => true >> "rah rah".is_a?(Numeric) => false |
So far so good. But my number is going to be stored in a String, and is_a? doesn’t actually look at the value of the String, it just checks its type.
1 2 |
>> "34".is_a?(Numeric) => false |
Ah. Fail. String isn’t a numerical class so even though it contains a numerical string, it’s always going to fail that test.
What about to_i?
I found to_i a little too clever for my needs…
1 2 3 4 5 6 7 8 9 |
>> "34".to_i => 34 # All good so far... >> "34DFDF".to_i => 34 # Ah. Fail. |
I don’t know why I typed 34DFDF. Sounds like a scary bra size….
Rails Forum to the rescue
Next I found a thread on railsforum.com which showed how to make an is_numeric? method and I decided to take that idea and extend the Object class to include this method on all objects. The idea being you could go :
1 2 3 4 5 6 7 8 |
>> 34.is_numeric? => true >> "34".is_numeric? => true >> "34DFDF".is_numeric? => false |
That’s perfect, so let’s write it….
Writing an is_numeric? method
I’m going to extend the Object class with this method so that it’s available everywhere. I’m using this in a rails app so I wrote the following in lib/core_extensions/object.rb
1 2 3 4 5 6 7 8 9 |
# lib/core_extensions/object.rb module CoreExtensions::Object def is_numeric? true if Float(self) rescue false end end Object.send(:include, CoreExtensions::Object) |
What it’s doing is seeing if the Float class can instantiate an instance of itself with the value of object. If object can’t be parsed to a Float then it throws an exception. If an exception is rescued then we know the object can’t be numerical so we return false. Simples.
Now we just need to require our little extension at the end of our environment.rb:
1 2 3 |
# config/environment.rb require 'core_extensions/object' |
... and we’re ready to go!
1 2 3 4 5 6 7 8 |
>> 34.is_numeric? => true >> "I'm not a number".is_numeric? => false >> "34".is_numeric? => true >> "34DFDF".is_numeric? => false |
Perfect.
Why extend Object and not String?
I had a ponder about that and figured it wouldn’t hurt if is_numeric? was called on non strings. By extending Object I can do things such as 34.is_numeric? which isn’t too shoddy. And I’m guessing if something nasty happens the rescue should catch anything bad. But if you can think of a good case for making it a String only method then please feel free to comment.
Also if there’s a better way of checking for numericalness then please post that in the comments. I couldn’t find anything built in to Ruby but I must admit I found that surprising.












I think adding class methods to Numeric would be a better idea than changing the Object class... You will be sure every libraries will work with your change
Example, 2 methods, for testing is an object is a number or if it represents a number
Numeric.number?("42") # => false Numeric.represents_number?("42") # => true@dohzya, thanks for taking the time to comment.
I can see how your method is safer and cleaner from a sense that numerical based functionality is grouped with the Numeric class.
But from an aesthetics point of view I prefer...
compared to
Although now I've written the two side by side I think perhaps there's a case for renaming the is_numeric? method to something like represents_number? because I'm not literally checking to see if the object is Numeric, which is what the name implies. It could be misleading.
Food for thought...
Does anyone know of any potential nastiness happening by putting this method in Object? I'm assuming that the rescue would catch anything unexpected? If there is risk involved with having the method in Object then perhaps putting a class method in Numeric is the best answer?
Just a quick observation: using Float(obj) works if the obj method has a to_f method. Usually, when this method is defined, is because it acts as numeric in some way
but it may not be exactly what you want.
Good point Ruben. Maybe some type checking in the method could eradicate that...
So now we're constraining the method to only work with Numeric and String classes. If it isn't one of these classes it returns false.
I'm not sure that's the most elegant way of writing the method but it cures the problem with the Time class you've pointed out. Thanks for bringing that one up.
This is a little neater than above...
Edit : Changed method name to represents_number? as it's a more accurate name for what the method is doing.
I have defined a similar method that seems to work well for me but haven't tested it extensively so I welcome any problems anyone sees with this:
So the basically idea is if it can go to an integer and back to a string and still be the same value then it is a number.
Another variant ....
def is_numeric?
self.is_a?(Numeric) or (self.is_a?(String) and !!Float(self))
rescue
false
end
@Eric
That doesn't seem to work for floats.
i.e. "0.4".is_number? => false
@Eric, I'm guessing because it uses to_i it isn't going to work with floats, as John points out.
@Vit, nice little addition. I didn't think of using the result of the condition as the return value and it gets rid of that ternary operator. I don't think we'll be able to get it much neater than that!
When dealing with integer-like strings, what's wrong with: ... foo.to_i.to_s == foo ... ?
Also, don't monkeypatch Object, for God's sake.
> what's wrong with: ... foo.to_i.to_s == foo ... ?
It's not the most self documenting statement in the world. Unless you comment that well the average noob wouldn't have a clue what's going on. Plus, as pointed out above, it falls over as soon as foo becomes a float which could be a problem depending on your situation.
> Also, don't monkeypatch Object, for God's sake.
@Joe, I'm interested why you think it would be such a bad idea in this case. If you've read the other comments I'm now constraining the method to only work with instances of String or Numeric so we shouldn't have any unexpected behaviour when called on different classes.
I hear a lot of people being wary of patching at high levels such as Object and sometimes I can see their point. But in this instance I'm not sure I see any massive disadvantage and would be really interested in what people see the pitfalls as being. I appreciate the advice not to do it, I'm just curious why not.
I had a desire to make my company, nevertheless I didn't have enough amount of cash to do it. Thank heaven my colleague told to utilize the <a href="http://lowest-rate-loans.com">loan</a>. Hence I received the term loan and made real my old dream.