Tester si la chaîne est un nombre dans Ruby on Rails

J’ai les éléments suivants dans mon contrôleur d’application:

def is_number?(object) true if Float(object) rescue false end 

et la condition suivante dans mon contrôleur:

 if myssortingng.is_number? end 

La condition lance une erreur de undefined method . Je suppose que j’ai défini is_number au mauvais endroit …?

Créer is_number? Méthode.

Créer une méthode d’assistance:

 def is_number? ssortingng true if Float(ssortingng) rescue false end 

Et puis appelez comme ça:

 my_ssortingng = '12.34' is_number?( my_ssortingng ) # => true 

Étendre la classe de Ssortingng .

Si vous voulez pouvoir appeler is_number? directement sur la chaîne au lieu de la passer en tant que paramètre à votre fonction d’assistance, vous devez alors définir is_number? comme une extension de la classe Ssortingng , comme ceci:

 class Ssortingng def is_number? true if Float(self) rescue false end end 

Et puis vous pouvez l’appeler avec:

 my_ssortingng.is_number? # => true 
 class Ssortingng def numeric? return true if self =~ /\A\d+\Z/ true if Float(self) rescue false end end p "1".numeric? # => true p "1.2".numeric? # => true p "5.4e-29".numeric? # => true p "12e20".numeric? # true p "1a".numeric? # => false p "1.2.3.4".numeric? # => false 

Voici une référence pour les moyens courants de résoudre ce problème. Notez que celui que vous devriez utiliser dépend probablement du ratio de faux cas attendus.

  1. S’ils sont relativement peu communs, le casting est certainement le plus rapide.
  2. Si les cas faux sont fréquents et que vous ne faites que vérifier les ints, la comparaison avec un état transformé est une bonne option.
  3. Si les cas faux sont communs et que vous vérifiez les flottants, regexp est probablement la voie à suivre

Si la performance n’a pas d’importance, utilisez ce que vous aimez. 🙂

Détails de contrôle entier:

 # 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.035311s require '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+$/ end end 

Flotteur vérifiant les détails:

 # 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.016494s require '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]+$/ end end 

S’appuyer sur l’exception soulevée n’est pas la solution la plus rapide, lisible et fiable.
Je ferais ce qui suit:

 my_ssortingng.should =~ /^[0-9]+$/ 

non, vous ne l’utilisez pas correctement. votre is_number? a un argument vous l’avez appelé sans argument

tu devrais faire is_number? (myssortingng)

Tl; dr: Utilisez une approche regex. Il est 39 fois plus rapide que l’approche de sauvetage dans la réponse acceptée et gère également des cas comme “1 000”

 def regex_is_number? ssortingng no_commas = ssortingng.gsub(',', '') matches = no_commas.match(/-?\d+(?:\.\d+)?/) if !matches.nil? && matches.size == 1 && matches[0] == no_commas true else false end end 

La réponse acceptée par @Jakob S fonctionne pour la plupart, mais attraper des exceptions peut être très lent. En outre, l’approche de secours échoue sur une chaîne telle que “1 000”.

Définissons les méthodes:

 def rescue_is_number? ssortingng true if Float(ssortingng) rescue false end def regex_is_number? ssortingng no_commas = ssortingng.gsub(',', '') matches = no_commas.match(/-?\d+(?:\.\d+)?/) if !matches.nil? && matches.size == 1 && matches[0] == no_commas true else false end end 

Et maintenant, quelques cas de test:

 test_cases = { true => ["5.5", "23", "-123", "1,234,123"], false => ["hello", "99designs", "(123)456-7890"] } 

Et un petit code pour exécuter les cas de test:

 test_cases.each do |expected_answer, cases| cases.each do |test_case| if rescue_is_number?(test_case) != expected_answer puts "**rescue_is_number? got #{test_case} wrong**" else puts "rescue_is_number? got #{test_case} right" end if regex_is_number?(test_case) != expected_answer puts "**regex_is_number? got #{test_case} wrong**" else puts "regex_is_number? got #{test_case} right" end end end 

Voici la sortie des cas de test:

 rescue_is_number? got 5.5 right regex_is_number? got 5.5 right rescue_is_number? got 23 right regex_is_number? got 23 right rescue_is_number? got -123 right regex_is_number? got -123 right **rescue_is_number? got 1,234,123 wrong** regex_is_number? got 1,234,123 right rescue_is_number? got hello right regex_is_number? got hello right rescue_is_number? got 99designs right regex_is_number? got 99designs right rescue_is_number? got (123)456-7890 right regex_is_number? got (123)456-7890 right 

Il est temps de faire des tests de performance:

 Benchmark.ips do |x| x.report("rescue") { test_cases.values.flatten.each { |c| rescue_is_number? c } } x.report("regex") { test_cases.values.flatten.each { |c| regex_is_number? c } } x.compare! end 

Et les résultats:

 Calculating ------------------------------------- rescue 128.000 i/100ms regex 4.649ki/100ms ------------------------------------------------- rescue 1.348k (±16.8%) i/s - 6.656k regex 52.113k (± 7.8%) i/s - 260.344k Comparison: regex: 52113.3 i/s rescue: 1347.5 i/s - 38.67x slower 

c’est comme ça que je le fais, mais je pense aussi qu’il doit y avoir une meilleure façon

 object.to_i.to_s == object || object.to_f.to_s == object 

Dans rails 4, vous devez mettre require File.expand_path('../../lib', __FILE__) + '/ext/ssortingng' dans votre config / application.rb

Si vous préférez ne pas utiliser les exceptions dans le cadre de la logique, vous pouvez essayer ceci:

 class Ssortingng def numeric? !!(self =~ /^-?\d+(\.\d*)?$/) end end 

Ou, si vous voulez qu’il fonctionne sur toutes les classes d’objects, remplacez la class Ssortingng par la class Object et convertissez-la en une chaîne: !!(self.to_s =~ /^-?\d+(\.\d*)?$/)

utilisez la fonction suivante:

 def is_numeric? val return val.try(:to_f).try(:to_s) == val end 

alors,

is_numeric? "1.2f" is_numeric? "1.2f" = faux

is_numeric? "1.2" is_numeric? "1.2" = true

is_numeric? "12f" is_numeric? "12f" = faux

is_numeric? "12" is_numeric? "12" = vrai

Quelle bêtise est cette solution?

 def is_number?(i) begin i+0 == i rescue TypeError false end end