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

szmergiel
źródło
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
steenslag
źródło
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

rado
źródło
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
Steve Craig
źródło
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.

David Foley
źródło
5
nil.to_s == 'true' #false
juliangonzalez
15
if value.to_s == 'true'
  true
elsif value.to_s == 'false'
  false
end
archana
źródło
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] 
Cary Swoveland
źródło
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
mnishiguchi
źródło
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
Jigar Bhatt
źródło
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

odpowiednik 8
źródło
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.

Felipe Funes
źródło
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
szmergiel
źródło
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.

Pavling
źródło
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.

Chad M.
źródło
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
Chris Flanagan
źródło