Ruby: Jak przekonwertować ciąg znaków na wartość logiczną


110

Mam wartość, która będzie jedną z czterech rzeczy: logiczna prawda, logiczna fałsz, ciąg „prawda” lub ciąg „fałsz”. Chcę przekonwertować ciąg na wartość logiczną, jeśli jest to ciąg, w przeciwnym razie pozostawić go niezmienionym. Innymi słowy:

„prawda” powinna stać się prawdą

„fałsz” powinien stać się fałszem

prawda powinna pozostać prawdziwa

fałsz powinien pozostać fałszywy


2
Czy wynik ma być jednym z dwóch wartości truei falseczy to wystarczy, jeśli wynik jest truthy lub falsey? Jeśli to drugie, to falsejest już falsey i oba truei 'true'są truthy, więc jedyną wartością, dla której wynik nie jest już prawidłowy, to 'false': if input == 'false' then true else input endpowinien to zrobić.
— Jörg W Mittag

To świetny komentarz, Jorg, jednak założyłbym, że dla niektórych aplikacji konieczne jest posiadanie wartości logicznej prawda lub fałsz, a nie tylko wartości, która jest prawdziwa lub fałszywa.
— szmergiel

2
Emery, jeśli trzeba zwrócić wartość logiczną można poprzedzić @ ekspresji Jorg z dwoma „biednymi”: !!(if input == 'false' then true else input end). Drugi !konwertuje zwracaną wartość na wartość logiczną, która jest przeciwieństwem tego, czego chcesz; pierwszy !następnie dokonuje korekty. Ta „sztuczka” istnieje od dawna. Nie wszystkim się to podoba.
— Cary Swoveland

Odpowiedzi:


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

3
Tak, @null, metoda to_s konwertuje wartość logiczną prawda lub fałsz na „prawda” lub „fałsz” i pozostawia wartość niezmienioną, jeśli pierwotnie był ciągiem. Teraz jesteśmy pewni, że jako ciąg znaków mamy „true” lub „false” ... i po prostu musimy użyć == check, czy ciąg jest równy „true”. Jeśli tak, to oryginalna wartość to prawda lub „prawda”. Jeśli tak nie jest, to oryginalna wartość była fałszywa, „fałszywa” lub coś zupełnie niezwiązanego.
— szmergiel

8
Ponieważ ciąg może być zwiększony / zatytułowany, zmniejszenie znaków zapewni dopasowanie:obj.to_s.downcase == 'true'
— TDH

1
Użyj, downcase!a przydzielisz 1 obiekt mniej. downcasepowieli istniejący ciąg. Kiedy Frozen String Literals stanie się domyślną opcją Rubiego, będzie to miało mniejsze znaczenie.
— danielricecodes

Pozwoliłem sobie zredagować odpowiedź, tak aby zawierała sugestię dotyczącą niedowładów! zgodnie z powyższymi komentarzami. Czytanie jest mniej eleganckie, ale jeśli nie masz pewności, z jakimi typami zmiennych pracujesz, większa niezawodność nigdy nie jest zła.
— emery

To nie zgłosi błędu, jeśli podasz mu złe dane, więc nie jest to świetne rozwiązanie, jeśli potrzebujesz obsługi błędów
— Toby 1 Kenobi

121

Jeśli używasz Rails 5, możesz to zrobić ActiveModel::Type::Boolean.new.cast(value).

W Railsach 4.2 użyj ActiveRecord::Type::Boolean.new.type_cast_from_user(value).

Zachowanie jest nieco inne, ponieważ w Railsach 4.2 sprawdzane są wartości prawda i fałsz. W Railsach 5 sprawdzane są tylko wartości fałsz - o ile wartości nie są zerowe lub nie pasują do wartości fałsz, przyjmuje się, że są prawdziwe. Fałszywe wartości są takie same w obu wersjach: FALSE_VALUES = [false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"]

Rails 5 Źródło: https://github.com/rails/rails/blob/5-1-stable/activemodel/lib/active_model/type/boolean.rb


1
Jest to pomocne, chociaż chciałbym, aby zestaw FALSE_VALUESw Railsach zawierał również „nie”.
— pjrebsch

3
@pjrebsch Dość łatwo załatać swoją aplikację. Po prostu dodaj ActiveRecord::Type::Boolean::FALSE_VALUES << "no"do inicjatora.
— thomasfedb

zwróć uwagę, że ActiveModel::Type::Boolean.new.cast(value)rozróżniana jest wielkość liter ... więc 'False' przyjmie wartość true, tak jak każdy inny ciąg z wyjątkiem 'false'. puste łańcuchy ''domyślnie mają wartość nil, a nie fałsz. ^^ cenne informacje dostarczone tutaj przez @thomasfedb na temat dostosowywania inicjatora
— frostini

1
ActiveModel::Type::Boolean.new.cast(nil)również powraca nil.
— Nikolay D

1
W Railsach 5.2.4 metoda sugerowana przez @thomasfedb nie działa, ponieważ ActiveRecord::Type::Boolean::FALSE_VALUESjest zamrożona.
— porusza się

24

Często używałem tego wzorca, aby rozszerzyć podstawowe zachowanie Rubiego, aby ułatwić sobie konwersję dowolnych typów danych na wartości logiczne, co bardzo ułatwia radzenie sobie z różnymi parametrami adresu URL itp.

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

Powiedzmy, że masz parametr, fooktórym może być:

  • liczba całkowita (0 oznacza fałsz, wszystkie inne są prawdziwe)
  • prawdziwy boolean (prawda / fałsz)
  • ciąg („prawda”, „fałsz”, „0”, „1”, „PRAWDA”, „FAŁSZ”)
  • zero

Zamiast używać kilku warunków, możesz po prostu zadzwonić, foo.to_booleana resztę magii wykona za Ciebie.

W Railsach dodaję to do inicjalizatora nazwanego core_ext.rbw prawie wszystkich moich projektach, ponieważ ten wzorzec jest tak powszechny.

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

a co z „t” i „f” „T” i „F”, „y” i „n”, „Y” i „N”?
— MrMesees

Działa to zbyt dobrze, np. Czy „kup” zaczyna się na „b”? "buy"=~/b/ => 0 Ale("buy"=~/b/).to_boolean => false
— Marcos

23

Nie myśl za dużo:

bool_or_string.to_s == "true"  

Więc,

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

Możesz również dodać „.downcase”, jeśli martwisz się dużymi literami.


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

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

10
Twój kod jako jednowierszowyvalue.to_s == 'true' ? true : false
— lód

20
@ sagarpandya82: Nigdy tego nie rób, to jest sprzeczne z celem operatora warunkowego: if true then true, if false then falseZgadnij co? Możesz go całkowicie usunąć! value.to_s == 'true' ? true : falsepowinno byćvalue.to_s == 'true'
— Eric Duminil

4
@EricDuminil całkowicie się zgadzam, w tym czasie był to błąd debiutanta.
— lód ツ

2
Zwróć uwagę, że ta odpowiedź zwróci nil, gdy wartość nie może zostać przekonwertowana, podczas gdy te jednowierszowe nigdy nie zawiodą i zawsze zwrócą fałsz, chyba że wartość to „prawda”. Oba są słusznymi podejściami i mogą być właściwą odpowiedzią na różne sytuacje, ale nie są takie same.
— Doodad

1
@AndreFigueiredo operator trójskładnikowy nie robi w tym przypadku nic. Spróbuj bez i porównaj wyniki.
— 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

W aplikacji rails 5.1 używam tego podstawowego rozszerzenia zbudowanego na ActiveRecord::Type::Boolean . Działa idealnie dla mnie, gdy deserializuję wartość logiczną z ciągu 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

zainicjuj podstawowe rozszerzenia

# 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

To jest doskonałe. Dokładnie to, czego szukałem.
— Doug

7

Praca w Railsach 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... Użyj to_s.downcase na wejściu to dobry pomysł
— Raphayol

5

W Railsach wolę używać ActiveModel::Type::Boolean.new.cast(value) jak wspomniano w innych odpowiedziach tutaj

Ale kiedy piszę zwykły Ruby lib. następnie używam hacka, w którym JSON.parse(standardowa biblioteka Ruby) zamieni ciąg znaków „prawda” na truei „fałsz” na false. Na przykład:

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

Przykład z aplikacji na żywo:

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

potwierdzone w Rubim 2.5.1


4

Mam mały hack do tego. JSON.parse('false')powróci falsei JSON.parse('true')wróci prawda. Ale to nie działa JSON.parse(true || false). Tak więc, jeśli używasz czegoś takiego JSON.parse(your_value.to_s), powinieneś osiągnąć swój cel w prosty, ale hakerski sposób.


3

Klejnot taki jak https://rubygems.org/gems/to_boolMożna użyć , ale można go łatwo zapisać w jednej linii za pomocą wyrażenia regularnego lub trójskładnika.

przykład wyrażenia regularnego:

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

potrójny przykład:

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

Zaletą metody regex jest to, że wyrażenia regularne są elastyczne i mogą pasować do wielu różnych wzorców. Na przykład, jeśli podejrzewasz, że zmienna może mieć dowolną wartość „True”, „False”, „T”, „F”, „t” lub „f”, możesz zmodyfikować to wyrażenie regularne:

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

2
Uwaga: \A/ \z to początek / koniec napisu, a ^/ $to początek / koniec linii. Więc jeśli var == "true\nwhatevs"wtedy boolean == true.
— cremno

To mi naprawdę pomogło i var.eql?('true') ? true : falsebardzo mi się podoba . Dzięki!
— Christian

3

Chociaż podoba mi się podejście haszujące (użyłem go w przeszłości do podobnych rzeczy), biorąc pod uwagę, że tak naprawdę zależy Ci tylko na dopasowywaniu prawdziwych wartości - ponieważ - wszystko inne jest fałszywe - możesz sprawdzić, czy jest włączona do tablicy:

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

lub jeśli inne wartości można uznać za prawdziwe:

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

musiałbyś zrobić inne rzeczy, jeśli oryginał valuemoże być mieszany:

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

ale znowu, dla twojego konkretnego opisu problemu, ostatni przykład może ci ujść na sucho jako rozwiązanie.


2

W szynach robiłem wcześniej coś takiego:

class ApplicationController < ActionController::Base
  # ...

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

  # ...
end

Co jest fajne, jeśli próbujesz dopasować swoje porównania ciągów logicznych w taki sam sposób, jak railsy dla twojej bazy danych.


0

Blisko tego, co już zostało opublikowane, ale bez zbędnego parametru:

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

stosowanie:

do_stuff = "true"

if do_stuff.true?
    #do stuff
end
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.