Ruby: Como converter uma string em booleano

107

Eu tenho um valor que será uma de quatro coisas: boolean true, boolean false, a string "true" ou a string "false". Desejo converter a string em booleano se for uma string, caso contrário, não a modificarei. Em outras palavras:

"verdadeiro" deve se tornar verdadeiro

"falso" deve se tornar falso

verdadeiro deve permanecer verdadeiro

falso deveria permanecer falso

esmeril
fonte
2
O resultado tem que ser um dos dois valores trueou falseou é suficiente se o resultado for verdadeiro ou falso? Se for o último, então falsejá é falsey, e ambos truee 'true'são verdadeiros, então o único valor para o qual o resultado ainda não está correto é 'false': if input == 'false' then true else input enddeveria fazê-lo.
Jörg W Mittag
Esse é um ótimo comentário Jorg, no entanto, eu presumiria que para algumas aplicações é necessário ter o valor booleano verdadeiro ou falso e não apenas um valor verdadeiro ou falso.
esmeril de
2
Emery, se você precisa de retornar um booleano você poderia preceder @ expressão de Jörg com dois "pobres": !!(if input == 'false' then true else input end). O segundo !converte o valor de retorno em um booleano que é o oposto do que você deseja; o primeiro !então faz a correção. Esse "truque" já existe há muito tempo. Nem todo mundo gosta disso.
Cary Swoveland

Respostas:

127
def true?(obj)
  obj.to_s.downcase == "true"
end
Steenslag
fonte
3
Sim, @null, o método to_s converte o booleano verdadeiro ou falso em "verdadeiro" ou "falso" e deixa o valor inalterado se for originalmente uma string. Agora temos certeza de ter "verdadeiro" ou "falso" como string ... e só precisamos usar == para verificar se a string é igual a "verdadeiro". Se for, o valor original era verdadeiro ou "verdadeiro". Se não for, o valor original era falso, "falso" ou algo totalmente não relacionado.
esmeril em
8
Uma vez que a sequência pode ser upcased / intitulado, downcasing irá garantir uma correspondência:obj.to_s.downcase == 'true'
TDH
1
Use downcase!e você alocará 1 objeto a menos. downcaseirá duplicar a string existente. Quando Frozen String Literals se tornar uma opção padrão do Ruby, isso terá menos importância.
danielricecodes
Tomei a liberdade de editar a resposta para incluir a sugestão de downcase! conforme os comentários acima. É menos elegante de ler, mas se você não tem certeza com quais tipos de variáveis ​​está trabalhando, mais robustez nunca é ruim.
esmeril de
Isso não relatará um erro se você fornecer dados incorretos, então não é uma ótima solução se você precisar de qualquer tratamento de erros
Toby 1 Kenobi
118

Se você usa Rails 5, você pode fazer ActiveModel::Type::Boolean.new.cast(value).

No Rails 4.2, use ActiveRecord::Type::Boolean.new.type_cast_from_user(value).

O comportamento é um pouco diferente, como no Rails 4.2, o valor verdadeiro e falso são verificados. No Rails 5, apenas valores falsos são verificados - a menos que os valores sejam nulos ou correspondam a um valor falso, ele é considerado verdadeiro. Os valores falsos são iguais nas duas versões: FALSE_VALUES = [false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"]

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

rado
fonte
1
Isso é útil, embora eu deseje que o conjunto de FALSE_VALUESem Rails também inclua "não".
pjrebsch
3
@pjrebsch Bastante simples de corrigir em seu aplicativo. Basta adicionar ActiveRecord::Type::Boolean::FALSE_VALUES << "no"a um inicializador.
thomasfedb,
note que ActiveModel::Type::Boolean.new.cast(value)diferencia maiúsculas de minúsculas ... então 'False' será avaliado como verdadeiro, assim como qualquer outra string, exceto 'false'. strings vazias ''padrão para nil, não false. ^^ informações valiosas fornecidas aqui por @thomasfedb sobre a personalização do inicializador
frostini
1
ActiveModel::Type::Boolean.new.cast(nil)também retorna nil.
Nikolay D
1
A partir do Rails 5.2.4 o método sugerido por @thomasfedb não funciona mais porque ActiveRecord::Type::Boolean::FALSE_VALUESestá congelado.
mudanças em
24

Eu frequentemente usei esse padrão para estender o comportamento central do Ruby para tornar mais fácil lidar com a conversão de tipos de dados arbitrários em valores booleanos, o que torna realmente fácil lidar com parâmetros variáveis ​​de 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

Então, digamos que você tenha um parâmetro fooque pode ser:

  • um inteiro (0 é falso, todos os outros são verdadeiros)
  • um verdadeiro booleano (verdadeiro / falso)
  • uma string ("verdadeiro", "falso", "0", "1", "VERDADEIRO", "FALSO")
  • nada

Em vez de usar um monte de condicionais, você pode apenas chamar foo.to_booleane ele fará o resto da mágica para você.

No Rails, eu adiciono isso a um inicializador chamado core_ext.rbem quase todos os meus projetos, já que esse padrão é muito comum.

## 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
fonte
que tal 't' e 'f' 'T' e 'F', 'y' e 'n', 'S' e 'N'?
MrMesees
Isso funciona muito bem, por exemplo. "Comprar" começa com "b"? "buy"=~/b/ => 0 Mas("buy"=~/b/).to_boolean => false
Marcos
23

Não pense muito:

bool_or_string.to_s == "true"  

Assim,

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

Você também pode adicionar ".downcase", se estiver preocupado com letras maiúsculas.

David Foley
fonte
5
nil.to_s == 'true' #false
juliangonzalez
15
if value.to_s == 'true'
  true
elsif value.to_s == 'false'
  false
end
Archana
fonte
10
Seu código de uma linhavalue.to_s == 'true' ? true : false
Sagar Pandya
20
@ sagarpandya82: Nunca faça isso, isso vai contra o propósito do operador condicional: if true then true, if false then falseAdivinha o quê? Você pode removê-lo completamente! value.to_s == 'true' ? true : falsedeveria servalue.to_s == 'true'
Eric Duminil
4
@EricDuminil concordo totalmente, erro de novato na época.
Sagar Pandya
2
Observe que essa resposta retornará nulo quando o valor não puder ser convertido, enquanto aqueles de uma linha nunca falharão e sempre retornarão falso, a menos que o valor seja 'verdadeiro'. Ambas são abordagens válidas e podem ser a resposta certa para diferentes situações, mas não são a mesma coisa.
Doodad
1
@AndreFigueiredo o operador ternário não faz nada neste caso. Experimente sem e compare os 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
fonte
7

Em um aplicativo Rails 5.1, eu uso esta extensão principal construída sobre ele ActiveRecord::Type::Boolean. Está funcionando perfeitamente para mim quando eu desserializo booleano da string 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 extensões principais

# 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
fonte
Isto é perfeito. Exatamente o que eu estava procurando.
Doug
5

Em Rails eu prefiro usar ActiveModel::Type::Boolean.new.cast(value)como mencionado em outras respostas aqui

Mas quando escrevo Ruby lib simples. então eu uso um hack onde JSON.parse(biblioteca Ruby padrão) irá converter string "true" para truee "false" para false. Por exemplo:

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

Exemplo de aplicativo ao vivo:

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

confirmado em Ruby 2.5.1

equivalente8
fonte
5

Trabalhando no 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
fonte
1
ActiveModel::Type::Boolean.new.cast("False") # => true... Usar to_s.downcase em sua entrada é uma boa ideia
Raphayol de
4

Eu tenho um pequeno hack para este. JSON.parse('false')retornará falsee JSON.parse('true')retornará verdadeiro. Mas isso não funciona com JSON.parse(true || false). Portanto, se você usar algo parecido JSON.parse(your_value.to_s), deverá atingir seu objetivo de uma forma simples, mas "hacky".

Felipe funes
fonte
3

Uma gema como https://rubygems.org/gems/to_bool pode ser usada, mas pode ser facilmente escrita em uma linha usando um regex ou ternário.

exemplo regex:

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

exemplo ternário:

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

A vantagem do método regex é que as expressões regulares são flexíveis e podem corresponder a uma ampla variedade de padrões. Por exemplo, se você suspeitar que var pode ser "Verdadeiro", "Falso", 'T', 'F', 't' ou 'f', poderá modificar o regex:

boolean = (var.to_s =~ /^[Tt].*$/i) == 0
esmeril
fonte
2
Nota: \A/ \z é o início / fim da string e ^/ $é o início / fim da linha. Então, se var == "true\nwhatevs"então boolean == true.
cremno,
Isso realmente me ajudou e eu gosto var.eql?('true') ? true : falsemuito. Obrigado!
Christian
3

Embora eu goste da abordagem de hash (já usei no passado para coisas semelhantes), dado que você realmente só se preocupa em combinar valores verdadeiros - já que - tudo o mais é falso - você pode verificar a inclusão em uma matriz:

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

ou se outros valores podem ser considerados verdadeiros:

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

você teria que fazer outras coisas se o original valuefosse maiúsculo:

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

mas, novamente, para sua descrição específica de seu problema, você poderia usar esse último exemplo como sua solução.

Pavling
fonte
2

Em trilhos, já fiz algo assim:

class ApplicationController < ActionController::Base
  # ...

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

  # ...
end

O que é bom se você está tentando combinar suas comparações de strings booleanas da mesma maneira que o rails faria para seu banco de dados.

Chad M
fonte
0

Perto do que já foi postado, mas sem o 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
fonte