Ruby: Як перетворити рядок у булева


107

У мене є значення, яке буде однією з чотирьох речей: булева істина, булева помилка, рядок "справжній" або рядок "хибна". Я хочу перетворити рядок у булеву, якщо вона є рядком, інакше залиште її без змін. Іншими словами:

"справжній" повинен стати істинним

"false" має стати хибним

правда повинна залишатися вірною

false має залишатися хибним


2
Чи повинен результат бути одним із двох значень, trueабо falseйого достатньо, якщо результат - триут або фальси? Якщо останні, то falseвже є фальси, і обидва, trueі 'true'є трибунними, тому єдине значення, для якого результат вже не правильний, це 'false': if input == 'false' then true else input endслід це робити.
Йорг W Міттаг

Це чудовий коментар Йорга, однак я б припустив, що для деяких додатків булеве значення має бути істинним або хибним, а не лише значенням, що є правдою чи хибністю.
наждак

2
Емері, якщо вам потрібно повертати логічне ви можете PREPEND @ вираз Йорг з двома «незаможними»: !!(if input == 'false' then true else input end). Другий !перетворює повернене значення в булеве, протилежне тому, що ви хочете; Перший !потім вносить виправлення. Цей "трюк" існує вже давно. Не всі це люблять.
Cary Swoveland

Відповіді:


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

3
Так, @null, метод to_s перетворює булеве значення true або false у "true" або "false" і залишає значення незмінним, якщо спочатку це був рядок. Зараз ми впевнені, що в якості рядка є "true" або "false" ... і нам просто потрібно використовувати == перевірити, чи рядок дорівнює "true". Якщо це так, то початкове значення було або істинним, або "істинним". Якщо ні, то початкове значення було помилковим, "хибним" або чимось абсолютно не пов'язаним.
наждак

8
Оскільки рядок може бути збільшений / заголовок, зменшення розміру забезпечить відповідність:obj.to_s.downcase == 'true'
TDH

1
Використовуйте downcase!і ви виділите на об’єкт менше 1. downcaseбуде дублювати існуючий рядок. Коли Frozen String Literals стане параметром за замовчуванням Ruby, це буде мати менше значення.
danielricecodes

Я взяв на себе сміття редагувати відповідь, щоб включити пропозицію про зменшення! відповідно до вищезазначених коментарів. Це менш елегантно для читання, але якщо ви не впевнені, з якими типами змінних ви працюєте, більш висока надійність ніколи не погана.
наждак

Цей звичай не повідомляє про помилку, якщо ви подаєте його погані дані, тож це не чудове рішення, якщо вам потрібна помилка
Toby 1 Kenobi

118

Якщо ви використовуєте Rails 5, ви можете це зробити ActiveModel::Type::Boolean.new.cast(value).

У Rails 4.2 використовуйте ActiveRecord::Type::Boolean.new.type_cast_from_user(value).

Поведінка дещо інша, тому що в Rails 4.2 перевіряється справжнє значення та помилкові значення. У Rails 5 перевіряються лише помилкові значення - якщо значення не дорівнюють нулю або не відповідають ложному значенню, воно вважається істинним. Помилкові значення однакові в обох версіях: FALSE_VALUES = [false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"]

Rails 5 Джерело: https://github.com/rails/rails/blob/5-1-stable/activemodel/lib/active_model/type/boolean.rb


1
Це корисно, хоча я хочу, щоб набір FALSE_VALUESв Rails також включав "ні".
pjrebsch

3
@pjrebsch Досить просто виправити у вашому додатку. Просто додайте ActiveRecord::Type::Boolean::FALSE_VALUES << "no"в ініціалізатор.
thomasfedb

зауважте, що ActiveModel::Type::Boolean.new.cast(value)залежно від регістру ... так що "False" буде оцінено як true, як і будь-який інший рядок, крім "false". порожні рядки ''за замовчуванням до нуля, не false. ^^ цінні відомості, надані тут @thomasfedb про налаштування ініціалізатора
frostini

1
ActiveModel::Type::Boolean.new.cast(nil)також повертається nil.
Микола Д

1
З Rails 5.2.4 метод, запропонований @thomasfedb, більше не працює, оскільки ActiveRecord::Type::Boolean::FALSE_VALUESзаморожений.
moveon

24

Я часто використовував цю схему для розширення основної поведінки Ruby, щоб полегшити роботу з перетворенням довільних типів даних у булі значення, що полегшує роботу з різними параметрами URL-адреси тощо.

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

Скажімо, у вас є параметр, fooякий може бути:

  • ціле число (0 помилкове, всі інші істинні)
  • справжній булевий (вірний / хибний)
  • рядок ("true", "false", "0", "1", "TRUE", "FALSE")
  • нуль

Замість того, щоб використовувати купу умовних умов, ви можете просто зателефонувати, foo.to_booleanі це зробить решту магії за вас.

У Rails я додаю це до ініціалізатора, названого core_ext.rbмайже у всіх моїх проектах, оскільки ця модель є такою поширеною.

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

як щодо 't' і 'f' 'T' & 'F', 'y' & 'n', 'Y' & 'N'?
MrMesees

Це працює надто добре, наприклад. Чи починається "купувати" з "б"? "buy"=~/b/ => 0 Але("buy"=~/b/).to_boolean => false
Маркос

23

Не думайте занадто багато:

bool_or_string.to_s == "true"  

Так,

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

Ви також можете додати ".case", якщо ви переживаєте з великої літери.


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

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

10
Ваш код як однолінійнийvalue.to_s == 'true' ? true : false
Sagar Pandya

20
@ sagarpandya82: Ніколи цього не роби, це перемагає призначення умовного оператора: if true then true, if false then falseВгадай, що? Ви можете повністю його зняти! value.to_s == 'true' ? true : falseслід просто бутиvalue.to_s == 'true'
Ерік Думініл

4
@EricDuminil абсолютно згоден, помилка новичка на той час.
Сагар Пандія

2
Зауважте, що ця відповідь поверне нуль, коли значення не вдасться перетворити, тоді як ці однолінійки ніколи не вийдуть з ладу і завжди повернуть помилкове значення, якщо значення не відповідає дійсності. Обидва є правильними підходами і можуть бути правильною відповіддю в різних ситуаціях, але вони не однакові.
Doodad

1
@AndreFigueiredо термінальний оператор нічого не робить у цьому випадку. Спробуйте без і порівняйте результати.
Ерік Думініл

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

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

7

У додатку rails 5.1 я використовую це основне розширення, побудоване поверх ActiveRecord::Type::Boolean. Це прекрасно працює для мене, коли я десериалізую булева з рядка 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

ініціалізувати розширення ядра

# 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

Це прекрасно. Саме те, що я шукав.
Дуг

5

У Rails я вважаю за краще використовувати, ActiveModel::Type::Boolean.new.cast(value)як згадується в інших відповідях тут

Але коли я пишу звичайний Ruby lib. тоді я використовую хак, де JSON.parse(стандартна бібліотека Ruby) перетворить рядок "true" в true"false" в false. Наприклад:

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

Приклад з живої програми:

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

підтверджено відповідно до Ruby 2.5.1


5

Робота в рейки 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... Корисна ідея
to_s.downcase

4

У мене є трохи хак для цього. JSON.parse('false')повернеться falseі JSON.parse('true')повернеться правдою. Але це не працює JSON.parse(true || false). Отже, якщо ви використовуєте щось подібне, JSON.parse(your_value.to_s)ви повинні досягти своєї мети простим, але хакітним способом.


3

Можна використовувати такий дорогоцінний камінь, як https://rubygems.org/gems/to_bool , але його можна легко записати в один рядок, використовуючи регулярний вираз або потрійну форму.

Приклад регулярного вираження:

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

потрійний приклад:

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

Перевага методу регулярного вираження полягає в тому, що регулярні вирази є гнучкими і можуть відповідати найрізноманітнішим шаблонам. Наприклад, якщо ви підозрюєте, що var може бути будь-яким із "True", "False", "T", "F", "t" або "f", ви можете змінити регулярний вираз:

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

2
Примітка: \A/ \z є початком / кінцем рядка і ^/ $є початком / кінцем рядка. Так що, якщо var == "true\nwhatevs"потім boolean == true.
cremno

Це мені дуже допомогло і мені var.eql?('true') ? true : falseдуже подобається . Дякую!
Крістіан

3

Хоча мені подобається хеш-підхід (я його використовував раніше для подібних матеріалів), враховуючи, що ви дійсно дбаєте про відповідність правдивих значень - оскільки - все інше помилкове - ви можете перевірити на включення до масиву:

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

або якщо інші значення можна вважати правдивими:

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

вам доведеться робити інші речі, якщо ваш оригінал valueможе бути змішаним корпусом:

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

але знову ж таки, для конкретного опису вашої проблеми ви можете піти з цього останнього прикладу як свого рішення.


2

У рейках я раніше робив щось подібне:

class ApplicationController < ActionController::Base
  # ...

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

  # ...
end

Що приємно, якщо ви намагаєтеся зіставити булеві струнні порівняння так само, як рейки для вашої бази даних.


0

Близький до того, що вже розміщено, але без зайвого параметра:

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

використання:

do_stuff = "true"

if do_stuff.true?
    #do stuff
end
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.