Test if string is a number in Ruby on Rails
Create is_number?
Method.
Create a helper method:
def is_number? string true if Float(string) rescue falseend
And then call it like this:
my_string = '12.34'is_number?( my_string )# => true
Extend String
Class.
If you want to be able to call is_number?
directly on the string instead of passing it as a param to your helper function, then you need to define is_number?
as an extension of the String
class, like so:
class String def is_number? true if Float(self) rescue false endend
And then you can call it with:
my_string.is_number?# => true
Here's a benchmark for common ways to address this problem. Note which one you should use probably depends on the ratio of false cases expected.
- If they are relatively uncommon casting is definitely fastest.
- If false cases are common and you are just checking for ints, comparison vs a transformed state is a good option.
- If false cases are common and you are checking floats, regexp is probably the way to go
If performance doesn't matter use what you like. :-)
Integer checking details:
# 1.9.3-p448## Calculating -------------------------------------# cast 57485 i/100ms# cast fail 5549 i/100ms# to_s 47509 i/100ms# to_s fail 50573 i/100ms# regexp 45187 i/100ms# regexp fail 42566 i/100ms# -------------------------------------------------# cast 2353703.4 (±4.9%) i/s - 11726940 in 4.998270s# cast fail 65590.2 (±4.6%) i/s - 327391 in 5.003511s# to_s 1420892.0 (±6.8%) i/s - 7078841 in 5.011462s# to_s fail 1717948.8 (±6.0%) i/s - 8546837 in 4.998672s# regexp 1525729.9 (±7.0%) i/s - 7591416 in 5.007105s# regexp fail 1154461.1 (±5.5%) i/s - 5788976 in 5.035311srequire 'benchmark/ips'int = '220000'bad_int = '22.to.2'Benchmark.ips do |x| x.report('cast') do Integer(int) rescue false end x.report('cast fail') do Integer(bad_int) rescue false end x.report('to_s') do int.to_i.to_s == int end x.report('to_s fail') do bad_int.to_i.to_s == bad_int end x.report('regexp') do int =~ /^\d+$/ end x.report('regexp fail') do bad_int =~ /^\d+$/ endend
Float checking details:
# 1.9.3-p448## Calculating -------------------------------------# cast 47430 i/100ms# cast fail 5023 i/100ms# to_s 27435 i/100ms# to_s fail 29609 i/100ms# regexp 37620 i/100ms# regexp fail 32557 i/100ms# -------------------------------------------------# cast 2283762.5 (±6.8%) i/s - 11383200 in 5.012934s# cast fail 63108.8 (±6.7%) i/s - 316449 in 5.038518s# to_s 593069.3 (±8.8%) i/s - 2962980 in 5.042459s# to_s fail 857217.1 (±10.0%) i/s - 4263696 in 5.033024s# regexp 1383194.8 (±6.7%) i/s - 6884460 in 5.008275s# regexp fail 723390.2 (±5.8%) i/s - 3613827 in 5.016494srequire 'benchmark/ips'float = '12.2312'bad_float = '22.to.2'Benchmark.ips do |x| x.report('cast') do Float(float) rescue false end x.report('cast fail') do Float(bad_float) rescue false end x.report('to_s') do float.to_f.to_s == float end x.report('to_s fail') do bad_float.to_f.to_s == bad_float end x.report('regexp') do float =~ /^[-+]?[0-9]*\.?[0-9]+$/ end x.report('regexp fail') do bad_float =~ /^[-+]?[0-9]*\.?[0-9]+$/ endend
class String def numeric? return true if self =~ /\A\d+\Z/ true if Float(self) rescue false endend p "1".numeric? # => truep "1.2".numeric? # => truep "5.4e-29".numeric? # => truep "12e20".numeric? # truep "1a".numeric? # => falsep "1.2.3.4".numeric? # => false