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.