Ruby: Comment convertir une chaîne en booléen

108

J'ai une valeur qui sera l'une des quatre choses suivantes: boolean true, boolean false, la chaîne "true" ou la chaîne "false". Je veux convertir la chaîne en booléen s'il s'agit d'une chaîne, sinon laissez-la inchangée. En d'autres termes:

"vrai" devrait devenir vrai

"false" devrait devenir faux

vrai devrait rester vrai

false doit rester faux

émeri
la source
2
Le résultat a être l' une des deux valeurs trueou falseou est - il suffisant si le résultat est truthy ou Falsey? Si ce dernier, alors falseest déjà faux, et les deux trueet 'true'sont véridiques, donc la seule valeur pour laquelle le résultat n'est pas déjà correct est 'false': if input == 'false' then true else input enddevrait le faire.
Jörg W Mittag
C'est un excellent commentaire Jorg, mais je suppose que pour certaines applications, il est nécessaire d'avoir la valeur booléenne true ou false et pas seulement une valeur vraie ou fausse.
emery
2
Emery, si vous avez besoin de retourner un booléen , vous pouvez préfixer @ expression de Jörg avec deux « déshérités »: !!(if input == 'false' then true else input end). Le second !convertit la valeur de retour en un booléen qui est l'opposé de ce que vous voulez; le premier !fait alors la correction. Ce "truc" existe depuis longtemps. Tout le monde ne l'aime pas.
Cary Swoveland

Réponses:

127
def true?(obj)
  obj.to_s.downcase == "true"
end
Steenslag
la source
3
Oui, @null, la méthode to_s convertit le booléen true ou false en "true" ou "false" et laisse la valeur inchangée s'il s'agissait à l'origine d'une chaîne. Maintenant, nous sommes certains d'avoir "true" ou "false" comme chaîne ... et nous devons juste utiliser == vérifier si la chaîne est égale à "true". Si c'est le cas, la valeur d'origine était soit true, soit "true". Si ce n'est pas le cas, la valeur d'origine était fausse, «fausse» ou quelque chose de totalement sans rapport.
emery
8
Puisque la chaîne peut être upcasée / titrée, le downcasing garantira une correspondance:obj.to_s.downcase == 'true'
TDH
1
Utilisez downcase!et vous allouerez 1 objet de moins. downcasedupliquera la chaîne existante. Lorsque Frozen String Literals devient une option par défaut de Ruby, cela importera moins.
danielricecodes
J'ai pris la liberté d'éditer la réponse pour inclure la suggestion de downcase! selon les commentaires ci-dessus. C'est moins élégant à lire, mais si vous n'êtes pas sûr des types de variables avec lesquels vous travaillez, plus de robustesse n'est jamais mauvais.
emery
Cela ne signalera pas une erreur si vous lui fournissez de mauvaises données, ce n'est donc pas une excellente solution si vous avez besoin d'une gestion d'erreur
Toby 1 Kenobi
118

Si vous utilisez Rails 5, vous pouvez faire ActiveModel::Type::Boolean.new.cast(value) .

Dans Rails 4.2, utilisez ActiveRecord::Type::Boolean.new.type_cast_from_user(value) .

Le comportement est légèrement différent, comme dans Rails 4.2, la vraie valeur et les fausses valeurs sont vérifiées. Dans Rails 5, seules les fausses valeurs sont vérifiées - à moins que les valeurs ne soient nulles ou correspondent à une valeur fausse, on suppose que c'est vrai. Les fausses valeurs sont les mêmes dans les deux versions: FALSE_VALUES = [false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"]

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

rado
la source
1
Ceci est utile, même si je souhaite que l'ensemble de FALSE_VALUESdans Rails comprenne également "non".
pjrebsch
3
@pjrebsch Assez simple à patcher dans votre application. Ajoutez simplement ActiveRecord::Type::Boolean::FALSE_VALUES << "no"à un initialiseur.
thomasfedb
notez qu'il ActiveModel::Type::Boolean.new.cast(value)est sensible à la casse ... donc 'False' sera évalué à vrai comme n'importe quelle autre chaîne sauf 'false'. les chaînes vides sont ''par défaut nulles et non fausses. ^^ informations précieuses fournies ici par @thomasfedb sur la personnalisation de l'initialiseur
frostini
1
ActiveModel::Type::Boolean.new.cast(nil)retourne également nil.
Nikolay D
1
Depuis Rails 5.2.4, la méthode suggérée par @thomasfedb ne fonctionne plus car elle ActiveRecord::Type::Boolean::FALSE_VALUESest gelée.
déplace le
24

J'ai fréquemment utilisé ce modèle pour étendre le comportement de base de Ruby afin de faciliter la conversion de types de données arbitraires en valeurs booléennes, ce qui facilite grandement la gestion des différents paramètres d'URL, 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

Alors disons que vous avez un paramètre fooqui peut être:

  • un entier (0 est faux, tous les autres sont vrais)
  • un vrai booléen (vrai / faux)
  • une chaîne ("vrai", "faux", "0", "1", "TRUE", "FALSE")
  • néant

Au lieu d'utiliser un tas de conditions, vous pouvez simplement appeler foo.to_boolean et il fera le reste de la magie pour vous.

Dans Rails, j'ajoute ceci à un initialiseur nommé core_ext.rbdans presque tous mes projets car ce modèle est si courant.

## 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
la source
qu'en est-il de «t» et «f» «T» et «F», «y» et «n», «Y» et «N»?
MrMesees
Cela fonctionne trop bien, par exemple. «Acheter» commence-t-il par un «b»? "buy"=~/b/ => 0 Mais("buy"=~/b/).to_boolean => false
Marcos
23

Ne réfléchissez pas trop:

bool_or_string.to_s == "true"  

Alors,

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

Vous pouvez également ajouter ".downcase" si vous vous inquiétez des majuscules.

David Foley
la source
5
nil.to_s == 'true' #false
juliangonzalez
15
if value.to_s == 'true'
  true
elsif value.to_s == 'false'
  false
end
archana
la source
10
Votre code en une seule lignevalue.to_s == 'true' ? true : false
Sagar Pandya
20
@ sagarpandya82: Ne faites jamais ça, cela va à l'encontre du but de l'opérateur conditionnel: if true then true, if false then falsedevinez quoi? Vous pouvez le supprimer complètement! value.to_s == 'true' ? true : falsedevrait juste êtrevalue.to_s == 'true'
Eric Duminil
4
@EricDuminil tout à fait d'accord, erreur de recrue à l'époque.
Sagar Pandya
2
Notez que cette réponse retournera nil lorsque la valeur ne peut pas être convertie alors que ces one-liners n'échoueront jamais et retourneront toujours false à moins que la valeur ne soit "true". Les deux sont des approches valables et peuvent être la bonne réponse à différentes situations, mais ce ne sont pas les mêmes.
Doodad
1
@AndreFigueiredo l'opérateur ternaire ne fait rien dans ce cas. Essayez sans et comparez les résultats.
Eric Duminil du
13
h = { "true"=>true, true=>true, "false"=>false, false=>false }

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

Dans une application rails 5.1, j'utilise cette extension de base construite sur ActiveRecord::Type::Boolean. Cela fonctionne parfaitement pour moi lorsque je désérialise le booléen de la chaîne 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

initialiser les extensions principales

# 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
la source
C'est parfait. Exactement ce que je cherchais.
Doug le
5

Dans Rails, je préfère utiliser ActiveModel::Type::Boolean.new.cast(value) comme mentionné dans d'autres réponses ici

Mais quand j'écris en clair Ruby lib. alors j'utilise un hack où JSON.parse(bibliothèque Ruby standard) convertira la chaîne "true" en trueet "false" en false. Par exemple:

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

Exemple d'application en direct:

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

confirmé sous Ruby 2.5.1

équivalent8
la source
5

Travailler dans les 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
la source
1
ActiveModel::Type::Boolean.new.cast("False") # => true... Utilisez to_s.downcase sur votre entrée est une bonne idée
Raphayol
4

J'ai un petit hack pour celui-ci. JSON.parse('false')reviendra falseet JSON.parse('true')retournera vrai. Mais cela ne fonctionne pas avec JSON.parse(true || false). Donc, si vous utilisez quelque chose comme JSON.parse(your_value.to_s)ça, vous devriez atteindre votre objectif d'une manière simple mais hacky.

Felipe Funes
la source
3

Un bijou comme https://rubygems.org/gems/to_bool peut être utilisé, mais il peut facilement être écrit sur une ligne en utilisant une expression régulière ou un ternaire.

exemple regex:

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

exemple ternaire:

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

L'avantage de la méthode regex est que les expressions régulières sont flexibles et peuvent correspondre à une grande variété de modèles. Par exemple, si vous pensez que var pourrait être l'un des "True", "False", "T", "F", "t" ou "f", vous pouvez modifier l'expression rationnelle:

boolean = (var.to_s =~ /^[Tt].*$/i) == 0
émeri
la source
2
Remarque: \A/ \z est le début / la fin de la chaîne et ^/ $est le début / la fin de la ligne. Alors si var == "true\nwhatevs"alors boolean == true.
cremno
Cela m'a vraiment aidé et j'aime var.eql?('true') ? true : falsebeaucoup. Merci!
Christian
3

Bien que j'aime l'approche de hachage (je l'ai utilisée dans le passé pour des choses similaires), étant donné que vous ne vous souciez vraiment que de la correspondance des valeurs de vérité - puisque - tout le reste est faux - vous pouvez vérifier l'inclusion dans un tableau:

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

ou si d'autres valeurs peuvent être considérées comme véridiques:

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

vous auriez à faire d'autres choses si votre original valuepourrait être une casse mixte:

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

mais encore une fois, pour votre description spécifique de votre problème, vous pourriez vous en tirer avec ce dernier exemple comme solution.

Pavage
la source
2

Dans les rails, j'ai déjà fait quelque chose comme ceci:

class ApplicationController < ActionController::Base
  # ...

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

  # ...
end

Ce qui est bien si vous essayez de faire correspondre vos comparaisons de chaînes booléennes de la même manière que les rails le feraient pour votre base de données.

Tchad M
la source
0

Proche de ce qui est déjà posté, mais sans le paramètre redondant:

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

usage:

do_stuff = "true"

if do_stuff.true?
    #do stuff
end
Chris Flanagan
la source