Ruby: So konvertieren Sie einen String in einen Booleschen Wert

107

Ich habe einen Wert, der eines von vier Dingen sein wird: boolean true, boolean false, die Zeichenfolge "true" oder die Zeichenfolge "false". Ich möchte den String in einen Booleschen Wert konvertieren, wenn es sich um einen String handelt, andernfalls möchte ich ihn unverändert lassen. Mit anderen Worten:

"wahr" sollte wahr werden

"falsch" sollte falsch werden

wahr sollte wahr bleiben

false sollte false bleiben

Schmirgel
quelle
2
Ist das Ergebnis hat einer der beiden Werte sein , trueoder , falseoder ist es ausreichend , wenn das Ergebnis truthy oder Falsey ist? Wenn letzteres falsebereits falsch ist und beide trueund 'true'wahr sind, so ist der einzige Wert, für den das Ergebnis nicht bereits korrekt ist , 'false': if input == 'false' then true else input endsollte es tun.
Jörg W Mittag
Das ist ein großartiger Kommentar, Jorg, aber ich würde annehmen, dass es für einige Anwendungen notwendig ist, den booleschen Wert wahr oder falsch zu haben und nicht nur einen Wert, der wahr oder falsch ist.
Schmirgel
2
Emery, wenn Sie einen Booleschen Wert zurückgeben müssen, können Sie @ Jörgs Ausdruck mit zwei "Nots" voranstellen : !!(if input == 'false' then true else input end). Der zweite !konvertiert den Rückgabewert in einen Booleschen Wert, der das Gegenteil von dem ist, was Sie wollen. der erste nimmt !dann die Korrektur vor. Dieser "Trick" gibt es schon lange. Nicht jeder mag es.
Cary Swoveland

Antworten:

125
def true?(obj)
  obj.to_s.downcase == "true"
end
steenslag
quelle
3
Ja, @null, die to_s-Methode konvertiert den booleschen Wert true oder false in "true" oder "false" und lässt den Wert unverändert, wenn er ursprünglich ein String war. Jetzt haben wir mit Sicherheit entweder "true" oder "false" als Zeichenfolge ... und wir müssen nur noch == verwenden, um zu überprüfen, ob die Zeichenfolge gleich "true" ist. Wenn dies der Fall ist, war der ursprüngliche Wert entweder wahr oder "wahr". Wenn dies nicht der Fall ist, war der ursprüngliche Wert falsch, "falsch" oder etwas völlig Unabhängiges.
Schmirgel
8
Da die Zeichenfolge möglicherweise in Groß- / Kleinschreibung geschrieben ist, wird durch Herabsetzung eine Übereinstimmung sichergestellt:obj.to_s.downcase == 'true'
TDH
1
Verwenden downcase!Sie und Sie weisen 1 Objekt weniger zu. downcasedupliziert die vorhandene Zeichenfolge. Wenn Frozen String Literals zu einer Ruby-Standardoption wird, ist dies weniger wichtig.
Danielricecodes
Ich habe mir erlaubt, die Antwort so zu bearbeiten, dass sie den Vorschlag eines Kleinbuchstaben enthält! gemäß den obigen Kommentaren. Das Lesen ist weniger elegant, aber wenn Sie nicht sicher sind, mit welchen Variablentypen Sie arbeiten, ist eine höhere Robustheit niemals schlecht.
Schmirgel
Dies meldet keinen Fehler, wenn Sie schlechte Daten eingeben, daher ist es keine gute Lösung, wenn Sie eine Fehlerbehandlung benötigen
Toby 1 Kenobi
118

Wenn Sie Rails 5 verwenden, können Sie dies tun ActiveModel::Type::Boolean.new.cast(value).

Verwenden Sie in Rails 4.2 ActiveRecord::Type::Boolean.new.type_cast_from_user(value).

Das Verhalten unterscheidet sich geringfügig, da in Rails 4.2 der wahre und der falsche Wert überprüft werden. In Rails 5 werden nur falsche Werte überprüft. Sofern die Werte nicht Null sind oder mit einem falschen Wert übereinstimmen, wird angenommen, dass sie wahr sind. Falsche Werte sind in beiden Versionen gleich: FALSE_VALUES = [false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"]

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

Rado
quelle
1
Dies ist hilfreich, obwohl ich mir wünsche, dass das Set FALSE_VALUESin Rails auch "no" enthält.
pjrebsch
3
@pjrebsch Ziemlich einfach in deine App zu patchen. Fügen Sie einfach ActiveRecord::Type::Boolean::FALSE_VALUES << "no"einen Initialisierer hinzu.
Thomasfedb
Beachten Sie, dass zwischen ActiveModel::Type::Boolean.new.cast(value)Groß- und Kleinschreibung unterschieden wird. Daher wird 'False' wie jede andere Zeichenfolge mit Ausnahme von 'false' als wahr ausgewertet. leere Zeichenfolgen sind ''standardmäßig null und nicht falsch. ^^ wertvolle Einblicke hier von @thomasfedb über die Anpassung des Initialisierers
Frostini
1
ActiveModel::Type::Boolean.new.cast(nil)kehrt auch zurück nil.
Nikolay D
1
Ab Rails 5.2.4 funktioniert die von @thomasfedb vorgeschlagene Methode nicht mehr, da sie ActiveRecord::Type::Boolean::FALSE_VALUESeingefroren ist.
Umzug
24

Ich habe dieses Muster häufig verwendet, um das Kernverhalten von Ruby zu erweitern und die Konvertierung beliebiger Datentypen in boolesche Werte zu vereinfachen, wodurch es wirklich einfach ist, mit unterschiedlichen URL-Parametern usw. umzugehen.

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

Nehmen wir also an, Sie haben einen Parameter, fooder sein kann:

  • eine ganze Zahl (0 ist falsch, alle anderen sind wahr)
  • ein wahrer Boolescher Wert (wahr / falsch)
  • eine Zeichenfolge ("wahr", "falsch", "0", "1", "WAHR", "FALSCH")
  • Null

Anstatt eine Reihe von Bedingungen zu verwenden, können Sie einfach anrufen foo.to_booleanund es wird den Rest der Magie für Sie erledigen.

In Rails füge ich dies einem Initialisierer hinzu, der core_ext.rbin fast allen meinen Projekten benannt ist, da dieses Muster so häufig vorkommt.

## 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
quelle
Was ist mit 't' und 'f' 'T' & 'F', 'y' & 'n', 'Y' & 'N'?
MrMesees
Das funktioniert zu gut, z. Beginnt "kaufen" mit einem "b"? "buy"=~/b/ => 0 Aber("buy"=~/b/).to_boolean => false
Marcos
22

Denk nicht zu viel nach:

bool_or_string.to_s == "true"  

So,

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

Sie können auch ".downcase" hinzufügen, wenn Sie sich Sorgen um Großbuchstaben machen.

David Foley
quelle
5
nil.to_s == 'true' #false
Juliangonzalez
15
if value.to_s == 'true'
  true
elsif value.to_s == 'false'
  false
end
Archana
quelle
10
Ihr Code als Einzeilervalue.to_s == 'true' ? true : false
Sagar Pandya
20
@ sagarpandya82: Mach das niemals, es macht den Zweck des bedingten Operators zunichte: Weißt du if true then true, if false then falsewas? Sie können es vollständig entfernen! value.to_s == 'true' ? true : falsesollte nur seinvalue.to_s == 'true'
Eric Duminil
4
@ EricDuminil absolut einverstanden, Anfängerfehler zu der Zeit.
Sagar Pandya
2
Beachten Sie, dass diese Antwort null zurückgibt, wenn der Wert nicht konvertiert werden kann, während diese Einzeiler niemals fehlschlagen und immer false zurückgeben, es sei denn, der Wert ist 'true'. Beide sind gültige Ansätze und können die richtige Antwort auf verschiedene Situationen sein, aber sie sind nicht gleich.
Doodad
1
@AndreFigueiredo der ternäre Operator macht in diesem Fall nichts. Versuchen Sie es ohne und vergleichen Sie die Ergebnisse.
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
quelle
7

In einer Rails 5.1-App verwende ich diese Kernerweiterung, auf der aufgebaut ist ActiveRecord::Type::Boolean. Es funktioniert perfekt für mich, wenn ich Boolesche Werte aus JSON-Zeichenfolgen deserialisiere.

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

Initialisieren Sie die Kernerweiterungen

# 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
quelle
Dies ist perfekt. Genau das, wonach ich gesucht habe.
Doug
5

In Rails bevorzuge ich die Verwendung, ActiveModel::Type::Boolean.new.cast(value)wie in anderen Antworten hier erwähnt

Aber wenn ich einfach Ruby lib schreibe. dann benutze ich einen Hack, bei dem JSON.parse(Standard-Ruby-Bibliothek) die Zeichenfolge "true" in trueund "false" in konvertiert false. Z.B:

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

Beispiel aus einer Live-Anwendung:

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

bestätigt unter Ruby 2.5.1

Äquivalent8
quelle
5

Arbeiten in Schienen 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
quelle
1
ActiveModel::Type::Boolean.new.cast("False") # => true... Die Verwendung von to_s.downcase für Ihre Eingabe ist eine gute Idee
Raphayol
4

Ich habe einen kleinen Hack für diesen. JSON.parse('false')wird zurückkehren falseund JSON.parse('true')wird true zurückgeben. Aber das funktioniert nicht mit JSON.parse(true || false). Wenn Sie also so etwas verwenden JSON.parse(your_value.to_s), sollten Sie Ihr Ziel auf einfache, aber hackige Weise erreichen.

Felipe Funes
quelle
3

Ein Edelstein wie https://rubygems.org/gems/to_bool kann verwendet werden, aber es kann leicht mit einem regulären Ausdruck oder einem ternären Text in einer Zeile geschrieben werden.

Regex-Beispiel:

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

ternäres Beispiel:

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

Der Vorteil der Regex-Methode besteht darin, dass reguläre Ausdrücke flexibel sind und einer Vielzahl von Mustern entsprechen können. Wenn Sie beispielsweise den Verdacht haben, dass var "True", "False", "T", "F", "t" oder "f" sein könnte, können Sie den regulären Ausdruck ändern:

boolean = (var.to_s =~ /^[Tt].*$/i) == 0
Schmirgel
quelle
2
Hinweis: \A/ \z ist Anfang / Ende der Zeichenfolge und ^/ $ist Anfang / Ende der Zeile. Also wenn var == "true\nwhatevs"dann boolean == true.
Cremno
Das hat mir var.eql?('true') ? true : falsesehr geholfen und ich mag es sehr. Vielen Dank!
Christian
3

Obwohl ich den Hash-Ansatz mag (ich habe ihn in der Vergangenheit für ähnliche Dinge verwendet), können Sie, da alles andere falsch ist, überprüfen, ob er in ein Array aufgenommen wurde:

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

oder wenn andere Werte als wahr angesehen werden könnten:

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

Sie valuemüssten andere Dinge tun, wenn Ihr Original ein gemischter Fall wäre:

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

Für Ihre spezifische Beschreibung Ihres Problems könnten Sie jedoch das letzte Beispiel als Lösung verwenden.

Pflasterung
quelle
2

In Schienen habe ich vorher so etwas gemacht:

class ApplicationController < ActionController::Base
  # ...

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

  # ...
end

Das ist schön, wenn Sie versuchen, Ihre booleschen Zeichenfolgenvergleiche auf dieselbe Weise abzugleichen, wie dies bei Rails für Ihre Datenbank der Fall wäre.

Chad M.
quelle
0

Nahe an dem, was bereits gepostet wurde, aber ohne den redundanten Parameter:

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

Verwendung:

do_stuff = "true"

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