Ruby: Cómo convertir una cadena a booleana

107

Tengo un valor que será una de cuatro cosas: booleano verdadero, booleano falso, la cadena "verdadero" o la cadena "falso". Quiero convertir la cadena en un booleano si es una cadena; de lo contrario, déjela sin modificar. En otras palabras:

"verdadero" debería convertirse en realidad

"falso" debería convertirse en falso

la verdad debería permanecer verdad

lo falso debe permanecer falso

esmeril
fuente
2
¿El resultado tiene que ser uno de los dos valores trueo falseo es suficiente si el resultado es Truthy o Falsey-? Si el último, entonces falseya es falso, y ambos truey 'true'son veraces, por lo que el único valor para el que el resultado no es ya correcto es 'false': if input == 'false' then true else input enddebería hacerlo.
Jörg W Mittag
Ese es un gran comentario, Jorg, sin embargo, supongo que para algunas aplicaciones es necesario tener el valor booleano verdadero o falso y no solo un valor verdadero o falso.
Emery
2
Emery, si usted necesita devolver un valor lógico que podría anteponer @ expresión de Jörg con dos "pobres": !!(if input == 'false' then true else input end). El segundo !convierte el valor de retorno a un booleano que es el opuesto al que desea; el primero !luego hace la corrección. Este "truco" existe desde hace mucho tiempo. No a todo el mundo le gusta.
Cary Swoveland

Respuestas:

127
def true?(obj)
  obj.to_s.downcase == "true"
end
Steenslag
fuente
3
Sí, @null, el método to_s convierte booleano verdadero o falso a "verdadero" o "falso" y deja el valor sin cambios si originalmente era una cadena. Ahora estamos seguros de tener "verdadero" o "falso" como cadena ... y solo necesitamos usar == verificar si la cadena es igual a "verdadero". Si es así, entonces el valor original era verdadero o "verdadero". Si no es así, el valor original era falso, "falso" o algo totalmente ajeno.
emery
8
Dado que la cadena puede ser subtitulada / titulada, downcasing asegurará una coincidencia:obj.to_s.downcase == 'true'
TDH
1
Use downcase!y asignará 1 objeto menos. downcaseduplicará la cadena existente. Cuando Frozen String Literals se convierta en una opción predeterminada de Ruby, esto importará menos.
danielricecodes
¡Me tomé la libertad de editar la respuesta para incluir la sugerencia de downcase! según los comentarios anteriores. Es menos elegante de leer, pero si no está seguro de con qué tipos de variables está trabajando, una mayor solidez nunca está mal.
emery
Esto no informará un error si lo alimenta con datos incorrectos, por lo que no es una gran solución si necesita algún manejo de errores
Toby 1 Kenobi
118

Si usa Rails 5, puede hacerlo ActiveModel::Type::Boolean.new.cast(value).

En Rails 4.2, utilice ActiveRecord::Type::Boolean.new.type_cast_from_user(value).

El comportamiento es ligeramente diferente, como en Rails 4.2, se verifican el valor verdadero y los valores falsos. En Rails 5, solo se verifican los valores falsos; a menos que los valores sean nulos o coincidan con un valor falso, se asume que es verdadero. Los valores falsos son los mismos en ambas versiones: FALSE_VALUES = [false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"]

Rails 5 Fuente: https://github.com/rails/rails/blob/5-1-stable/activemodel/lib/active_model/type/boolean.rb

rado
fuente
1
Esto es útil, aunque me gustaría que el conjunto de FALSE_VALUESen Rails también incluyera "no".
pjrebsch
3
@pjrebsch Bastante simple de parchear en su aplicación. Solo agregue ActiveRecord::Type::Boolean::FALSE_VALUES << "no"a un inicializador.
thomasfedb
tenga en cuenta que ActiveModel::Type::Boolean.new.cast(value)distingue entre mayúsculas y minúsculas ... por lo que 'False' se evaluará como verdadero al igual que cualquier otra cadena excepto 'falso'. cadenas vacías por ''defecto a nulo, no falso. ^^ información valiosa proporcionada aquí por @thomasfedb sobre la personalización del inicializador
frostini
1
ActiveModel::Type::Boolean.new.cast(nil)también regresa nil.
Nikolay D
1
A partir de Rails 5.2.4, el método sugerido por @thomasfedb ya no funciona porque ActiveRecord::Type::Boolean::FALSE_VALUESestá congelado.
moveson
24

Con frecuencia he usado este patrón para extender el comportamiento central de Ruby para facilitar la conversión de tipos de datos arbitrarios en valores booleanos, lo que hace que sea realmente fácil lidiar con parámetros de URL variables, etc.

class String
  def to_boolean
    ActiveRecord::Type::Boolean.new.cast(self)
  end
end

class NilClass
  def to_boolean
    false
  end
end

class TrueClass
  def to_boolean
    true
  end

  def to_i
    1
  end
end

class FalseClass
  def to_boolean
    false
  end

  def to_i
    0
  end
end

class Integer
  def to_boolean
    to_s.to_boolean
  end
end

Entonces digamos que tiene un parámetro fooque puede ser:

  • un número entero (0 es falso, todos los demás son verdaderos)
  • un booleano verdadero (verdadero / falso)
  • una cadena ("verdadero", "falso", "0", "1", "VERDADERO", "FALSO")
  • nulo

En lugar de usar un montón de condicionales, puedes simplemente llamar foo.to_booleany hará el resto de la magia por ti.

En Rails, agrego esto a un inicializador nombrado core_ext.rben casi todos mis proyectos ya que este patrón es muy común.

## EXAMPLES

nil.to_boolean     == false
true.to_boolean    == true
false.to_boolean   == false
0.to_boolean       == false
1.to_boolean       == true
99.to_boolean      == true
"true".to_boolean  == true
"foo".to_boolean   == true
"false".to_boolean == false
"TRUE".to_boolean  == true
"FALSE".to_boolean == false
"0".to_boolean     == false
"1".to_boolean     == true
true.to_i          == 1
false.to_i         == 0
Steve Craig
fuente
¿Qué pasa con 't' y 'f' 'T' y 'F', 'y' & 'n', 'Y' y 'N'?
MrMesees
Esto funciona demasiado bien, por ejemplo. ¿La "compra" comienza con una "b"? "buy"=~/b/ => 0 Pero("buy"=~/b/).to_boolean => false
Marcos
23

No pienses demasiado:

bool_or_string.to_s == "true"  

Entonces,

"true".to_s == "true"   #true
"false".to_s == "true"  #false 
true.to_s == "true"     #true
false.to_s == "true"    #false

También puede agregar ".downcase", si le preocupan las letras mayúsculas.

David Foley
fuente
5
nil.to_s == 'true' #false
juliangonzalez
15
if value.to_s == 'true'
  true
elsif value.to_s == 'false'
  false
end
archana
fuente
10
Su código como una sola líneavalue.to_s == 'true' ? true : false
Sagar Pandya
20
@ sagarpandya82: Nunca hagas eso, frustra el propósito del operador condicional: if true then true, if false then false¿Adivina qué? ¡Puedes eliminarlo por completo! value.to_s == 'true' ? true : falsedebería servalue.to_s == 'true'
Eric Duminil
4
@EricDuminil absolutamente de acuerdo, error de novato en ese momento.
Sagar Pandya
2
Tenga en cuenta que esta respuesta devolverá cero cuando el valor no se pueda convertir, mientras que esas frases ingeniosas nunca fallarán y siempre devolverán falso a menos que el valor sea 'verdadero'. Ambos son enfoques válidos y pueden ser la respuesta correcta a diferentes situaciones, pero no son lo mismo.
Doodad
1
@AndreFigueiredo el operador ternario no hace nada en este caso. Pruébelo sin y compare los resultados.
Eric Duminil
13
h = { "true"=>true, true=>true, "false"=>false, false=>false }

["true", true, "false", false].map { |e| h[e] }
  #=> [true, true, false, false] 
Cary Swoveland
fuente
7

En una aplicación rails 5.1, utilizo esta extensión central construida sobre ActiveRecord::Type::Boolean. Funciona perfectamente para mí cuando deserializo un booleano de una cadena JSON.

https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html

# app/lib/core_extensions/string.rb
module CoreExtensions
  module String
    def to_bool
      ActiveRecord::Type::Boolean.new.deserialize(downcase.strip)
    end
  end
end

inicializar extensiones centrales

# config/initializers/core_extensions.rb
String.include CoreExtensions::String

rspec

# spec/lib/core_extensions/string_spec.rb
describe CoreExtensions::String do
  describe "#to_bool" do
    %w[0 f F false FALSE False off OFF Off].each do |falsey_string|
      it "converts #{falsey_string} to false" do
        expect(falsey_string.to_bool).to eq(false)
      end
    end
  end
end
mnishiguchi
fuente
Esto es perfecto. Exactamente lo que estaba buscando.
Doug
5

En Rails prefiero usar ActiveModel::Type::Boolean.new.cast(value) como se menciona en otras respuestas aquí

Pero cuando escribo Ruby lib. luego uso un truco donde JSON.parse(biblioteca estándar de Ruby) convertirá la cadena "verdadero" en truey "falso" en false. P.ej:

require 'json'
azure_cli_response = `az group exists --name derrentest`  # => "true\n"
JSON.parse(azure_cli_response) # => true

azure_cli_response = `az group exists --name derrentesttt`  # => "false\n"
JSON.parse(azure_cli_response) # => false

Ejemplo de aplicación en vivo:

require 'json'
if JSON.parse(`az group exists --name derrentest`)
  `az group create --name derrentest --location uksouth`
end

confirmado bajo Ruby 2.5.1

equivalente8
fuente
5

Trabajando en Rails 5

ActiveModel::Type::Boolean.new.cast('t')     # => true
ActiveModel::Type::Boolean.new.cast('true')  # => true
ActiveModel::Type::Boolean.new.cast(true)    # => true
ActiveModel::Type::Boolean.new.cast('1')     # => true
ActiveModel::Type::Boolean.new.cast('f')     # => false
ActiveModel::Type::Boolean.new.cast('0')     # => false
ActiveModel::Type::Boolean.new.cast('false') # => false
ActiveModel::Type::Boolean.new.cast(false)   # => false
ActiveModel::Type::Boolean.new.cast(nil)     # => nil
Jigar Bhatt
fuente
1
ActiveModel::Type::Boolean.new.cast("False") # => true... Usar to_s.downcase en su entrada es una buena idea
Raphayol
4

Tengo un pequeño truco para este. JSON.parse('false')volverá falsey JSON.parse('true')volverá verdadero. Pero esto no funciona con JSON.parse(true || false). Entonces, si usa algo así JSON.parse(your_value.to_s), debería lograr su objetivo de una manera simple pero hacky.

Felipe Funes
fuente
3

Una joya como https://rubygems.org/gems/to_bool puede usar , pero se puede escribir fácilmente en una línea usando una expresión regular o un ternario.

ejemplo de expresiones regulares:

boolean = (var.to_s =~ /^true$/i) == 0

ejemplo ternario:

boolean = var.to_s.eql?('true') ? true : false

La ventaja del método regex es que las expresiones regulares son flexibles y pueden coincidir con una amplia variedad de patrones. Por ejemplo, si sospecha que var podría ser cualquiera de "True", "False", 'T', 'F', 't' o 'f', entonces puede modificar la expresión regular:

boolean = (var.to_s =~ /^[Tt].*$/i) == 0
esmeril
fuente
2
Nota: \A/ \z es el comienzo / final de la cadena y ^/ $es el comienzo / final de la línea. Entonces si var == "true\nwhatevs"entonces boolean == true.
cremno
Esto realmente me ayudó y me gusta var.eql?('true') ? true : falsemucho. ¡Gracias!
Christian
3

Aunque me gusta el enfoque hash (lo he usado en el pasado para cosas similares), dado que solo te importa hacer coincidir los valores de verdad, ya que todo lo demás es falso, puedes verificar la inclusión en una matriz:

value = [true, 'true'].include?(value)

o si otros valores pudieran considerarse veraces:

value = [1, true, '1', 'true'].include?(value)

tendrías que hacer otras cosas si tu original valuepudiera ser un caso mixto:

value = value.to_s.downcase == 'true'

pero nuevamente, para su descripción específica de su problema, podría salirse con la suya con ese último ejemplo como solución.

Pavling
fuente
2

En rieles, anteriormente hice algo como esto:

class ApplicationController < ActionController::Base
  # ...

  private def bool_from(value)
    !!ActiveRecord::Type::Boolean.new.type_cast_from_database(value)
  end
  helper_method :bool_from

  # ...
end

Lo cual es bueno si está tratando de hacer coincidir sus comparaciones de cadenas booleanas de la misma manera que lo harían los rieles para su base de datos.

Chad M
fuente
0

Cerca de lo que ya está publicado, pero sin el parámetro redundante:

class String
    def true?
        self.to_s.downcase == "true"
    end
end

uso:

do_stuff = "true"

if do_stuff.true?
    #do stuff
end
Chris Flanagan
fuente