Ruby: как преобразовать строку в логическое значение

110

У меня есть значение, которое будет одним из четырех: логическое истина, логическое ложь, строка «истина» или строка «ложь». Я хочу преобразовать строку в логическое значение, если это строка, в противном случае оставьте ее неизменной. Другими словами:

"правда" должно стать правдой

"ложь" должна стать ложной

правда должна оставаться верной

ложь должна оставаться ложной

Эмери
источник
2
Есть ли результат есть быть один из двух значений trueили falseили достаточно , если результат truthy или falsey? Если последнее, then falseуже является ложным, и оба trueи 'true'являются правдивыми, поэтому единственное значение, для которого результат еще не верен, это 'false': if input == 'false' then true else input endshould do it.
Jörg W Mittag
Это отличный комментарий, Йорг, однако я бы предположил, что для некоторых приложений необходимо иметь логическое значение true или false, а не только значение, которое является истинным или ложным.
наждак
2
Эмери, если вам нужно возвращать булево вы можете PREPEND @ выражение Йорг с двумя «неимущими»: !!(if input == 'false' then true else input end). Второй !преобразует возвращаемое значение в логическое значение, противоположное тому, что вы хотите; первый !затем вносит исправления. Этот «трюк» существует уже давно. Не всем это нравится.
Cary Swoveland

Ответы:

129
def true?(obj)
  obj.to_s.downcase == "true"
end
Steenslag
источник
3
Да, @null, метод to_s преобразует логическое значение true или false в «true» или «false» и оставляет значение неизменным, если оно изначально было строкой. Теперь мы уверены, что в качестве строки будет либо «истина», либо «ложь» ... и нам просто нужно использовать ==, чтобы проверить, равна ли строка «истине». Если это так, то исходное значение было либо истинным, либо «истинным». Если это не так, то исходное значение было ложным, «ложным» или чем-то совершенно не связанным.
наждачная
8
Поскольку строка может быть преобразована в верхний регистр / названа, нижний регистр обеспечит совпадение:obj.to_s.downcase == 'true'
TDH
1
Используйте, downcase!и вы выделите на 1 объект меньше. downcaseбудет дублировать существующую строку. Когда Frozen String Literals станет параметром Ruby по умолчанию, это будет иметь меньшее значение.
danielricecodes
Я взял на себя смелость отредактировать ответ, чтобы включить предложение о падении! согласно приведенным выше комментариям. Его менее элегантно читать, но если вы не уверены, с какими типами переменных вы работаете, большая надежность никогда не бывает плохой.
наждачной
Это не будет сообщать об ошибке, если вы скармливаете ему неверные данные, поэтому это не
Тоби 1 Кеноби
121

Если вы используете 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_VALUESRails также входило «нет».
pjrebsch
3
@pjrebsch Довольно просто исправить в вашем приложении. Просто добавьте ActiveRecord::Type::Boolean::FALSE_VALUES << "no"в инициализатор.
thomasfedb 05
обратите внимание, что ActiveModel::Type::Boolean.new.cast(value)это чувствительно к регистру ... поэтому 'False' будет оцениваться как true, как и любая другая строка, кроме 'false'. пустые строки по ''умолчанию равны nil, а не false. ^^ Ценная информация, предоставленная здесь @thomasfedb по настройке инициализатора
frostini
1
ActiveModel::Type::Boolean.new.cast(nil)также возвращается nil.
Николай Д
1
Начиная с Rails 5.2.4 метод, предложенный @thomasfedb, больше не работает, потому что ActiveRecord::Type::Boolean::FALSE_VALUESон заморожен.
переезд
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 - ложь, все остальные - истина)
  • истинное логическое значение (истина / ложь)
  • строка («истина», «ложь», «0», «1», «ИСТИНА», «ЛОЖЬ»)
  • ноль

Вместо того, чтобы использовать кучу условных операторов, вы можете просто позвонить, 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

Вы также можете добавить «.downcase», если вас беспокоят заглавные буквы.

Дэвид Фоули
источник
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
ice ツ
20
@ sagarpandya82: Никогда не делайте этого, это противоречит цели условного оператора: if true then true, if false then falseУгадайте, что? Вы можете полностью удалить это! value.to_s == 'true' ? true : falseпросто должно бытьvalue.to_s == 'true'
Эрик Думинил
4
@EricDuminil абсолютно согласен, ошибка новичка в то время.
ice ツ
2
Обратите внимание, что этот ответ вернет nil, если значение не может быть преобразовано, в то время как эти однострочники никогда не завершатся ошибкой и всегда возвращают false, если значение не равно «true». Оба подхода являются действительными и могут быть правильным ответом в разных ситуациях, но это не одно и то же.
Doodad
1
@AndreFigueiredo, тернарный оператор в этом случае ничего не делает. Попробуйте и сравните результаты.
Эрик
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
mnishiguchi
источник
Это потрясающе. Именно то, что я искал.
Дуг
7

Работа в Rails 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 в вашем вводе - хорошая идея
Raphayol
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

эквивалент8
источник
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очень нравится . Спасибо!
Christian
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

Что хорошо, если вы пытаетесь сопоставить свои логические сравнения строк так же, как rails для вашей базы данных.

Чад М
источник
0

Близко к тому, что уже опубликовано, но без избыточного параметра:

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

использование:

do_stuff = "true"

if do_stuff.true?
    #do stuff
end
Крис Фланаган
источник