Ruby: Cara mengubah string menjadi boolean

107

Saya memiliki nilai yang akan menjadi salah satu dari empat hal: boolean true, boolean false, string "true", atau string "false". Saya ingin mengubah string menjadi boolean jika itu adalah string, jika tidak biarkan tidak diubah. Dengan kata lain:

"benar" harus menjadi kenyataan

"false" harus menjadi salah

benar harus tetap benar

salah harus tetap salah

amril
sumber
2
Apakah hasilnya harus menjadi salah satu dari dua nilai trueatau falseatau itu cukup jika hasilnya adalah truthy atau falsey? Jika yang terakhir, maka falsesudah salah, dan keduanya truedan 'true'benar, jadi satu-satunya nilai yang hasilnya belum benar, adalah 'false': if input == 'false' then true else input endharus melakukannya.
Jörg W Mittag
Itu adalah komentar yang bagus Jorg, namun saya akan berasumsi bahwa untuk beberapa aplikasi perlu memiliki nilai boolean benar atau salah dan bukan hanya nilai yang benar atau salah.
ampelas
2
Emery, jika Anda perlu untuk mengembalikan boolean Anda bisa tambahkan @ ekspresi Jörg dengan dua "miskin": !!(if input == 'false' then true else input end). Yang kedua !mengubah nilai kembali menjadi boolean yang berlawanan dengan yang Anda inginkan; yang pertama !kemudian melakukan koreksi. "Trik" ini sudah ada sejak lama. Tidak semua orang menyukainya.
Cary Swoveland

Jawaban:

127
def true?(obj)
  obj.to_s.downcase == "true"
end
steenslag.dll
sumber
3
Ya, @null, metode to_s mengonversi boolean true atau false menjadi "true" atau "false" dan membiarkan nilai tidak berubah jika aslinya berupa string. Sekarang kita pasti memiliki "true" atau "false" sebagai string ... dan kita hanya perlu menggunakan == memeriksa apakah string tersebut sama dengan "true". Jika ya, maka nilai aslinya bisa benar atau "benar". Jika tidak, maka nilai aslinya salah, "salah", atau sesuatu yang sama sekali tidak terkait.
ampelas
8
Karena string dapat di-upcased / diberi judul, downcasing akan memastikan kecocokan:obj.to_s.downcase == 'true'
TDH
1
Gunakan downcase!dan Anda akan mengalokasikan 1 objek lebih sedikit. downcaseakan menduplikasi string yang ada. Ketika Frozen String Literals menjadi opsi default Ruby, ini tidak terlalu penting.
danielricecodes
Saya mengambil kebebasan mengedit jawaban untuk memasukkan saran huruf kecil! sesuai komentar di atas. Ini kurang elegan untuk dibaca, tetapi jika Anda tidak yakin jenis variabel apa yang Anda gunakan, lebih banyak ketahanan tidak pernah buruk.
ampelas
Ini tidak akan melaporkan kesalahan jika Anda memberinya data yang buruk, jadi ini bukan solusi yang bagus jika Anda memerlukan penanganan kesalahan
Toby 1 Kenobi
118

Jika Anda menggunakan Rails 5, Anda bisa melakukannya ActiveModel::Type::Boolean.new.cast(value).

Di Rails 4.2, gunakan ActiveRecord::Type::Boolean.new.type_cast_from_user(value).

Perilakunya sedikit berbeda, seperti pada Rails 4.2, nilai benar dan nilai salah diperiksa. Di Rails 5, hanya nilai salah yang diperiksa - kecuali nilainya nihil atau cocok dengan nilai salah, ini dianggap benar. Nilai salah sama di kedua versi: FALSE_VALUES = [false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"]

Rails 5 Sumber: https://github.com/rails/rails/blob/5-1-stable/activemodel/lib/active_model/type/boolean.rb

rado
sumber
1
Ini berguna, meskipun saya berharap set FALSE_VALUESdi Rails juga menyertakan "no".
pjrebsch
3
@pjrebsch Cukup mudah untuk ditambal di aplikasi Anda. Tambahkan saja ActiveRecord::Type::Boolean::FALSE_VALUES << "no"ke penginisialisasi.
thomasfedb
perhatikan bahwa ActiveModel::Type::Boolean.new.cast(value)case sensitive ... jadi 'False' akan dievaluasi menjadi true seperti halnya string lainnya kecuali 'false'. string kosong ''default ke nil, bukan false. ^^ wawasan berharga diberikan di sini oleh @thomasfedb tentang penyesuaian penginisialisasi
frostini
1
ActiveModel::Type::Boolean.new.cast(nil)juga kembali nil.
Nikolay D
1
Pada Rails 5.2.4, metode yang disarankan oleh @thomasfedb tidak lagi berfungsi karena ActiveRecord::Type::Boolean::FALSE_VALUESdibekukan.
bergerak
24

Saya sering menggunakan pola ini untuk memperluas perilaku inti Ruby agar lebih mudah menangani konversi tipe data arbitrer ke nilai boolean, yang membuatnya sangat mudah untuk menangani berbagai parameter URL, dll.

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

Jadi katakanlah Anda memiliki parameter fooyang dapat berupa:

  • sebuah integer (0 salah, yang lainnya benar)
  • sebuah boolean benar (benar / salah)
  • string ("true", "false", "0", "1", "TRUE", "FALSE")
  • nol

Alih-alih menggunakan sekumpulan persyaratan, Anda bisa menelepon foo.to_booleandan itu akan melakukan sisanya untuk Anda.

Di Rails, saya menambahkan ini ke penginisialisasi yang dinamai core_ext.rbdi hampir semua proyek saya karena pola ini sangat umum.

## 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
sumber
bagaimana dengan 't' dan 'f' 'T' & 'F', 'y' & 'n', 'Y' & 'N'?
MrMesees
Ini bekerja terlalu baik, mis. Apakah "beli" dimulai dengan "b"? "buy"=~/b/ => 0 Tapi("buy"=~/b/).to_boolean => false
Marcos
23

Jangan terlalu banyak berpikir:

bool_or_string.to_s == "true"  

Begitu,

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

Anda juga dapat menambahkan ".downcase", jika Anda mengkhawatirkan huruf kapital.

David Foley
sumber
5
nil.to_s == 'true' #false
juliangonzalez
15
if value.to_s == 'true'
  true
elsif value.to_s == 'false'
  false
end
archana
sumber
10
Kode Anda sebagai satu barisvalue.to_s == 'true' ? true : false
Sagar Pandya
20
@ sagarpandya82: Jangan pernah melakukan itu, ini akan mengalahkan tujuan operator bersyarat: if true then true, if false then falseCoba tebak? Anda dapat menghapusnya sepenuhnya! value.to_s == 'true' ? true : falseseharusnya hanyavalue.to_s == 'true'
Eric Duminil
4
@EricDuminil sangat setuju, kesalahan pemula pada saat itu.
Sagar Pandya
2
Perhatikan bahwa jawaban ini akan mengembalikan nihil ketika nilai tidak dapat dikonversi sementara satu baris tersebut tidak akan pernah gagal dan selalu mengembalikan salah kecuali nilainya adalah 'benar'. Keduanya adalah pendekatan yang valid dan mungkin merupakan jawaban yang tepat untuk situasi yang berbeda, tetapi keduanya tidak sama.
Doodad
1
@AndreFigueiredo operator terner tidak melakukan apa pun dalam kasus ini. Coba tanpa, dan bandingkan hasilnya.
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
sumber
7

Di aplikasi rel 5.1, saya menggunakan ekstensi inti yang dibangun di atasnya ActiveRecord::Type::Boolean . Ini bekerja dengan sempurna untuk saya ketika saya menghilangkan boolean dari string 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

menginisialisasi ekstensi inti

# 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
sumber
Ini sempurna. Persis apa yang saya cari.
Doug
5

Di Rails saya lebih suka menggunakan ActiveModel::Type::Boolean.new.cast(value) seperti yang disebutkan dalam jawaban lain di sini

Tetapi ketika saya menulis lib Ruby biasa. kemudian saya menggunakan hack di mana JSON.parse(library Ruby standar) akan mengubah string "true" menjadi truedan "false" menjadi false. Misalnya:

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

Contoh dari aplikasi langsung:

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

dikonfirmasi di bawah Ruby 2.5.1

setara8
sumber
5

Bekerja di 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
Jigar Bhatt
sumber
1
ActiveModel::Type::Boolean.new.cast("False") # => true... Gunakan to_s.downcase pada masukan Anda adalah ide yang bagus
Raphayol
4

Saya memiliki sedikit retasan untuk yang satu ini. JSON.parse('false')akan kembali falsedan JSON.parse('true')akan kembali benar. Tapi ini tidak berhasil JSON.parse(true || false). Jadi, jika Anda menggunakan sesuatu seperti JSON.parse(your_value.to_s)itu harus mencapai tujuan Anda dengan cara yang sederhana namun hacky.

Felipe Funes
sumber
3

Permata seperti https://rubygems.org/gems/to_bool dapat digunakan, tetapi dapat dengan mudah ditulis dalam satu baris menggunakan regex atau terner.

contoh regex:

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

contoh terner:

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

Keuntungan metode regex adalah ekspresi reguler fleksibel dan dapat mencocokkan berbagai pola. Misalnya, jika Anda mencurigai bahwa var dapat berupa salah satu dari "True", "False", 'T', 'F', 't', atau 'f', maka Anda dapat memodifikasi regex:

boolean = (var.to_s =~ /^[Tt].*$/i) == 0
amril
sumber
2
Catatan: \A/ \z adalah awal / akhir string dan ^/ $adalah awal / akhir baris. Jadi kalau var == "true\nwhatevs"begitu boolean == true.
cremno
Ini sangat membantu saya dan saya var.eql?('true') ? true : falsesangat menyukainya. Terima kasih!
Christian
3

Meskipun saya suka pendekatan hash (saya pernah menggunakannya di masa lalu untuk hal-hal serupa), mengingat Anda hanya benar-benar peduli tentang pencocokan nilai kebenaran - karena - yang lainnya salah - Anda dapat memeriksa penyertaan dalam array:

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

atau jika nilai-nilai lain dapat dianggap benar:

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

Anda harus melakukan hal-hal lain jika aslinya valuemungkin casing campuran:

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

tetapi sekali lagi, untuk deskripsi spesifik Anda tentang masalah Anda, Anda dapat menggunakan contoh terakhir sebagai solusi Anda.

Pavling
sumber
2

Di rel, saya sebelumnya telah melakukan sesuatu seperti ini:

class ApplicationController < ActionController::Base
  # ...

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

  # ...
end

Yang bagus jika Anda mencoba mencocokkan perbandingan string boolean dengan cara yang sama seperti rail untuk database Anda.

Chad M
sumber
0

Dekat dengan apa yang sudah diposting, tetapi tanpa parameter yang berlebihan:

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

pemakaian:

do_stuff = "true"

if do_stuff.true?
    #do stuff
end
Chris Flanagan
sumber