2011-11-09 13 views
5

credo di avere una buona risposta a questo problema, ma ho voluto fare in modo rubino-philes non hanno un modo molto migliore per farlo.convertire il valore di ingresso per intero o float, a seconda dei casi Utilizzando Rubino

Fondamentalmente, in una stringa di input, desidero convertire la stringa in un intero, se del caso, o un galleggiante, se del caso. Altrimenti, basta restituire la stringa.

vi posto la mia risposta qui sotto, ma mi piacerebbe sapere se c'è un modo migliore là fuori.

Es:

to_f_or_i_or_s("0523.49") #=> 523.49 
to_f_or_i_or_s("0000029") #=> 29 
to_f_or_i_or_s("kittens") #=> "kittens" 

risposta

9

Eviterei di usare regex quando possibile in Ruby. È notoriamente lento.

def to_f_or_i_or_s(v) 
    ((float = Float(v)) && (float % 1.0 == 0) ? float.to_i : float) rescue v 
end 

# Proof of Ruby's slow regex 
def regex_float_detection(input) 
    input.match('\.') 
end 

def math_float_detection(input) 
    input % 1.0 == 0 
end 

n = 100_000 
Benchmark.bm(30) do |x| 
    x.report("Regex") { n.times { regex_float_detection("1.1") } } 
    x.report("Math") { n.times { math_float_detection(1.1) } } 
end 

#          user  system  total  real 
# Regex       0.180000 0.000000 0.180000 ( 0.181268) 
# Math       0.050000 0.000000 0.050000 ( 0.048692) 

Un punto di riferimento più completo:

def wattsinabox(input) 
    input.match('\.').nil? ? Integer(input) : Float(input) rescue input.to_s 
end 

def jaredonline(input) 
    ((float = Float(input)) && (float % 1.0 == 0) ? float.to_i : float) rescue input 
end 

def muistooshort(input) 
    case(input) 
    when /\A\s*[+-]?\d+\.\d+\z/ 
     input.to_f 
    when /\A\s*[+-]?\d+(\.\d+)?[eE]\d+\z/ 
     input.to_f 
    when /\A\s*[+-]?\d+\z/ 
     input.to_i  
    else 
     input 
    end 
end 

n = 1_000_000 
Benchmark.bm(30) do |x| 
    x.report("wattsinabox") { n.times { wattsinabox("1.1") } } 
    x.report("jaredonline") { n.times { jaredonline("1.1") } } 
    x.report("muistooshort") { n.times { muistooshort("1.1") } } 
end 

#          user  system  total  real 
# wattsinabox      3.600000 0.020000 3.620000 ( 3.647055) 
# jaredonline      1.400000 0.000000 1.400000 ( 1.413660) 
# muistooshort     2.790000 0.010000 2.800000 ( 2.803939) 
5
def to_f_or_i_or_s(v) 
    v.match('\.').nil? ? Integer(v) : Float(v) rescue v.to_s 
end 
2

Un mucchio di regex potrebbe essere una buona idea se si desidera gestire numeri in notazione scientifica (che String#to_f fa):

def to_f_or_i_or_s(v) 
    case(v) 
    when /\A\s*[+-]?\d+\.\d+\z/ 
     v.to_f 
    when /\A\s*[+-]?\d+(\.\d+)?[eE]\d+\z/ 
     v.to_f 
    when /\A\s*[+-]?\d+\z/ 
     v.to_i  
    else 
     v 
    end 
end 

Si potrebbe schiacciare entrambi i casi to_f in una regex se si desidera.

Questo ovviamente non funzionerà quando alimentato '3,14159' in un locale che utilizza una virgola come separatore decimale.

+0

Nella mia funzione, che verrebbe effettivamente restituito come stringa, poiché la conversione float fallirebbe e genererebbe un'eccezione e quindi il salvataggio eseguirà to_s e ritornerà. – WattsInABox

+0

@WattsInABox: Giusto sei (mostra quanto uso 'Float'). Ma hai ancora una notazione scientifica di cui preoccuparti. –

+0

Questo è tru, @muistooshort. Grazie! – WattsInABox

2

Dipende requisiti di sicurezza.

def to_f_or_i_or_s s 
    eval(s) rescue s 
end 
0

Ho usato questo metodo

def to_f_or_i_or_s(value) 
    return value if value[/[a-zA-Z]/] 

    i = value.to_i 
    f = value.to_f 

    i == f ? i : f 
    end 
0

CSV ha convertitori che fanno questo.

require "csv" 
strings = ["0523.49", "29","kittens"] 
strings.each{|s|p s.parse_csv(converters: :numeric).first} 

#523.49 
#29 
#"kittens" 

Tuttavia, per qualche motivo converte "00029" ad un galleggiante.

Problemi correlati