Ruby: come convertire una stringa in booleano


107

Ho un valore che sarà una delle quattro cose: boolean true, boolean false, la stringa "true" o la stringa "false". Voglio convertire la stringa in un booleano se è una stringa, altrimenti lasciarla non modificata. In altre parole:

"vero" dovrebbe diventare vero

"false" dovrebbe diventare falso

vero dovrebbe rimanere vero

false dovrebbe rimanere false


2
Ha il risultato deve essere uno dei due valori trueo falseo è sufficiente se il risultato è truthy o Falsey? Se quest'ultimo, allora falseè già falso, ed entrambi truee 'true'sono veri, quindi l'unico valore per il quale il risultato non è già corretto, è 'false': if input == 'false' then true else input enddovrebbe farlo.
Jörg W Mittag

Questo è un ottimo commento Jorg, tuttavia presumo che per alcune applicazioni sia necessario avere il valore booleano vero o falso e non solo un valore vero o falso.
smeriglio

2
Emery, se è necessario restituire un valore booleano si può anteporre @ espressione di Jörg con due "poveri": !!(if input == 'false' then true else input end). Il secondo !converte il valore restituito in un booleano che è l'opposto di quello che vuoi; il primo !poi fa la correzione. Questo "trucco" esiste da molto tempo. Non a tutti piace.
Cary Swoveland

Risposte:


127
def true?(obj)
  obj.to_s.downcase == "true"
end

3
Sì, @null, il metodo to_s converte booleano vero o falso in "vero" o "falso" e lascia il valore invariato se originariamente era una stringa. Ora siamo certi di avere "true" o "false" come stringa ... e dobbiamo solo usare == verificare se la stringa è uguale a "true". Se lo è, il valore originale era vero o "vero". In caso contrario, il valore originale era falso, "falso" o qualcosa di totalmente estraneo.
smerigliato

8
Poiché la stringa può essere upcased / titolata, downcasing garantirà una corrispondenza:obj.to_s.downcase == 'true'
TDH

1
Usa downcase!e assegnerai 1 oggetto in meno. downcaseduplicherà la stringa esistente. Quando Frozen String Literals diventa un'opzione predefinita di Ruby, questo avrà meno importanza.
danielricecodes

Mi sono preso la libertà di modificare la risposta per includere il suggerimento di downcase! come da commenti sopra. È meno elegante da leggere, ma se non sei sicuro di quali tipi di variabili stai lavorando, una maggiore robustezza non è mai male.
smeriglio

Questo non segnalerà un errore se fornisci dati errati, quindi non è un'ottima soluzione se hai bisogno di una gestione degli errori
Toby 1 Kenobi

118

Se usi Rails 5, puoi farlo ActiveModel::Type::Boolean.new.cast(value).

In Rails 4.2, usa ActiveRecord::Type::Boolean.new.type_cast_from_user(value).

Il comportamento è leggermente diverso, poiché in Rails 4.2 vengono controllati il ​​valore vero e quelli falsi. In Rails 5, vengono controllati solo i valori falsi - a meno che i valori non siano nulli o corrispondano a un valore falso, si presume che sia vero. I valori falsi sono gli stessi in entrambe le versioni: FALSE_VALUES = [false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"]

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


1
Questo è utile, anche se vorrei che il set di FALSE_VALUESRails includesse anche "no".
pjrebsch

3
@pjrebsch Abbastanza semplice da applicare nella tua app. Basta aggiungere ActiveRecord::Type::Boolean::FALSE_VALUES << "no"a un inizializzatore.
thomasfedb

nota che ActiveModel::Type::Boolean.new.cast(value)fa distinzione tra maiuscole e minuscole ... quindi "False" restituirà true come qualsiasi altra stringa ad eccezione di "false". le stringhe vuote vengono impostate di ''default su nil, non false. ^^ preziosa intuizione fornita qui da @thomasfedb sulla personalizzazione dell'inizializzatore
frostini

1
ActiveModel::Type::Boolean.new.cast(nil)ritorna anche nil.
Nikolay D

1
A partire da Rails 5.2.4 il metodo suggerito da @thomasfedb non funziona più perché ActiveRecord::Type::Boolean::FALSE_VALUESè congelato.
sposta il

24

Ho spesso usato questo modello per estendere il comportamento principale di Ruby per rendere più facile gestire la conversione di tipi di dati arbitrari in valori booleani, il che rende davvero facile gestire parametri URL variabili, ecc.

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

Quindi diciamo che hai un parametro fooche può essere:

  • un numero intero (0 è falso, tutti gli altri sono veri)
  • un vero booleano (vero / falso)
  • una stringa ("true", "false", "0", "1", "TRUE", "FALSE")
  • zero

Invece di usare un mucchio di condizionali, puoi semplicemente chiamare foo.to_booleane farà il resto della magia per te.

In Rails, lo aggiungo a un inizializzatore denominato core_ext.rbin quasi tutti i miei progetti poiché questo modello è così comune.

## 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

che dire di "t" e "f" "T" e "F", "y" e "n", "Y" e "N"?
MrMesees

Funziona troppo bene, ad es. "Compra" inizia con una "b"? "buy"=~/b/ => 0 Ma("buy"=~/b/).to_boolean => false
Marcos

23

Non pensare troppo:

bool_or_string.to_s == "true"  

Così,

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

Puoi anche aggiungere ".downcase", se sei preoccupato per le lettere maiuscole.


5
nil.to_s == 'true' #false
juliangonzalez

15
if value.to_s == 'true'
  true
elsif value.to_s == 'false'
  false
end

10
Il tuo codice come battuta singolavalue.to_s == 'true' ? true : false
Sagar Pandya

20
@ sagarpandya82: Non farlo mai, vanifica lo scopo dell'operatore condizionale: if true then true, if false then falseindovina cosa? Puoi rimuoverlo completamente! value.to_s == 'true' ? true : falsedovrebbe essere solovalue.to_s == 'true'
Eric Duminil

4
@EricDuminil assolutamente d'accordo, errore da principiante al momento.
Sagar Pandya

2
Nota che questa risposta restituirà zero quando il valore non può essere convertito, mentre quelle battute non falliranno mai e restituiranno sempre false a meno che il valore non sia "vero". Entrambi sono approcci validi e possono essere la risposta giusta a situazioni diverse, ma non sono la stessa cosa.
Doodad

1
@AndreFigueiredo l'operatore ternario non fa nulla in questo caso. Prova senza e confronta i risultati.
Eric Duminil

13
h = { "true"=>true, true=>true, "false"=>false, false=>false }

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

7

In un'app per rails 5.1, utilizzo questa estensione principale costruita sopra ActiveRecord::Type::Boolean. Funziona perfettamente per me quando deserializzo booleano dalla stringa 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

inizializza le estensioni principali

# 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

Questo è perfetto. Esattamente quello che stavo cercando.
Doug

5

In Rails preferisco usare ActiveModel::Type::Boolean.new.cast(value)come menzionato in altre risposte qui

Ma quando scrivo plain Ruby lib. quindi utilizzo un hack in cui JSON.parse(libreria Ruby standard) converte la stringa "true" in truee "false" in false. Per esempio:

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

Esempio dall'applicazione live:

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

confermato in Ruby 2.5.1


5

Lavorare in 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

1
ActiveModel::Type::Boolean.new.cast("False") # => true... Usa to_s.downcase per il tuo input è una buona idea
Raphayol

4

Ho un piccolo trucco per questo. JSON.parse('false')tornerà falsee JSON.parse('true')tornerà vero. Ma questo non funziona con JSON.parse(true || false). Quindi, se usi qualcosa del genere JSON.parse(your_value.to_s), dovresti raggiungere il tuo obiettivo in modo semplice ma hacky.


3

Una gemma come https://rubygems.org/gems/to_bool può essere utilizzata, ma può essere facilmente scritta in una riga utilizzando un'espressione regolare o ternaria.

esempio di regex:

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

esempio ternario:

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

Il vantaggio del metodo regex è che le espressioni regolari sono flessibili e possono corrispondere a un'ampia varietà di modelli. Ad esempio, se sospetti che var possa essere "True", "False", 'T', 'F', 't' o 'f', puoi modificare la regex:

boolean = (var.to_s =~ /^[Tt].*$/i) == 0

2
Nota: \A/ \z è l'inizio / fine della stringa e ^/ $è l'inizio / fine della riga. Quindi se var == "true\nwhatevs"allora boolean == true.
cremno

Questo mi ha davvero aiutato e mi piace var.eql?('true') ? true : falsemolto. Grazie!
Christian

3

Anche se mi piace l'approccio hash (l'ho usato in passato per cose simili), dato che ti interessa solo far corrispondere valori veritieri - poiché - tutto il resto è falso - puoi verificare l'inclusione in un array:

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

o se altri valori potrebbero essere ritenuti veritieri:

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

dovresti fare altre cose se l'originale valuepotrebbe essere in maiuscolo:

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

ma ancora una volta, per la tua descrizione specifica del tuo problema, potresti farla franca con l'ultimo esempio come soluzione.


2

In rails, ho già fatto qualcosa del genere:

class ApplicationController < ActionController::Base
  # ...

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

  # ...
end

Il che è bello se stai cercando di abbinare i tuoi confronti di stringhe booleane allo stesso modo di rails per il tuo database.


0

Vicino a quanto già pubblicato, ma senza il parametro ridondante:

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

utilizzo:

do_stuff = "true"

if do_stuff.true?
    #do stuff
end
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.