Apa cara elegan di Ruby untuk mengetahui apakah suatu variabel adalah Hash atau Array?

144

Untuk memeriksa apa @some_var, saya melakukan a

if @some_var.class.to_s == 'Hash' 

Saya yakin ada cara yang lebih elegan untuk memeriksa apakah @some_varitu a Hashatau Array.

drhyde
sumber
1
Andrew: Saya memanggil API dan di JSON saya mendapatkan kembali, jika ada banyak hasil saya mendapatkan Array tetapi jika hanya ada satu saya mendapatkan Hash daripada satu elemen Array. Apakah ada penyerang yang lebih baik daripada melakukan pemeriksaan Array vs Hash?
drhyde
2
Jadi, Anda mendapatkan salah satunya [result_hash, another_result_hash]atau single_result_hash? Siapa pun yang membuat API itu tidak melakukan pekerjaan dengan baik!
Andrew Grimm
2
Anda benar, Andrew, dan saya yakin jauh lebih mudah untuk membuat orang yang menulis API memperbaikinya daripada menguji hash versus array.
Olivier 'Ölbaum' Scherler
Saya memiliki situasi yang sama dengan @drhyde. Dalam kasus saya, pihak ke-3 adalah HTTParty yang setelah mengurai file XML tidak memiliki cara untuk memutuskan apa cara terbaik untuk menangani situasi di mana suatu elemen dapat memiliki anak 1-n.
Dirty Henry
Anda harus menonton screencast Avdi Grimms: rubytapas.com/2016/10/17/episode-451-advanced-class-membership dan mungkin membuat cabo menjawab yang diterima.
Alexander Presber

Jawaban:

271

Anda bisa melakukan:

@some_var.class == Hash

atau juga sesuatu seperti:

@some_var.is_a?(Hash)

Perlu dicatat bahwa "is_a?" metode ini benar jika kelas berada di mana saja di pohon leluhur objek. contohnya:

@some_var.is_a?(Object)  # => true

hal di atas benar jika @some_var adalah turunan dari hash atau kelas lain yang berasal dari Object. Jadi, jika Anda ingin pencocokan ketat pada jenis kelas, gunakan == atau instance_of? Metode mungkin adalah apa yang Anda cari.

Pete
sumber
20
is_a?adalah pilihan terbaik, karena ia juga mengembalikan truesubclass.
Fábio Batista
Dan, tentu saja, Anda juga mengabaikan tanda kurung, jadi @som_var.is_a? Hashbekerja juga :)
Gus Shortz
7
Hati-hati, ini benar-benar bisa mengacaukan Anda jika seseorang akhirnya memberikan Anda contoh ActiveSupport :: HashWithIndifferentAccess. Yang merespons seperti hash, tetapi sebenarnya bukan hash. :(
unflores
Saya juga ingin lebih banyak jawaban yang merinci tentang mengetik bebek, karena penulis asli tampaknya sedang mencari cara ruby ​​untuk melakukan pemeriksaan jenis.
NobodysNightmare
3
@unflores ActiveSupport::HashWithIndifferentAccessmewarisi dari Hash, karenanya @some_var.is_a?(Hash)akan mengembalikan true dalam kasus ini juga. (Saya baru saja memiliki pertanyaan yang sama dan kasus HashWithIndifferentAccess dan agak bingung dengan komentar Anda ... jadi saya ingin mengklarifikasi)
Stilzk1n
38

Pertama-tama, jawaban terbaik untuk pertanyaan literal adalah

Hash === @some_var

Tetapi pertanyaan itu seharusnya dijawab dengan menunjukkan bagaimana melakukan bebek-mengetik di sini. Itu tergantung sedikit pada jenis bebek yang Anda butuhkan.

@some_var.respond_to?(:each_pair)

atau

@some_var.respond_to?(:has_key?)

atau bahkan

@some_var.respond_to?(:to_hash)

mungkin benar tergantung aplikasinya.

cabo
sumber
25

Biasanya di ruby ​​ketika Anda mencari "tipe" Anda sebenarnya menginginkan "tipe bebek" atau "apakah dukun seperti bebek?". Anda akan melihat apakah itu menanggapi metode tertentu:

@some_var.respond_to?(:each)

Anda dapat mengulangi @some_var karena merespons: masing-masing

Jika Anda benar-benar ingin mengetahui jenisnya dan jika itu adalah Hash atau Array maka Anda dapat melakukan:

["Hash", "Array"].include?(@some_var.class)  #=> check both through instance class
@some_var.kind_of?(Hash)    #=> to check each at once
@some_var.is_a?(Array)   #=> same as kind_of
Brandon
sumber
Ini tidak akan berfungsi jika kode Anda bergantung pada urutan data (misalnya jika Anda menggunakan each_with_index). Urutan elemen diimplementasikan secara berbeda antara hash dan array dan berbeda antara versi ruby (. Intertwingly.net/slides/2008/oscon/ruby19/22 )
juan2raid
1
@ juan2raid: Jika urutan itu penting, panggil sortdulu.
Andrew Grimm
Contoh kedua @Brandon harus mengonversi kelas menjadi string. <br/>["Hash", "Array"].include?(@some_var.class.to_s) #=> check both through instance class
OBCENEIKON
12
Hash === @some_var #=> return Boolean

ini juga dapat digunakan dengan pernyataan kasus

case @some_var
when Hash
   ...
when Array
   ...
end
GutenYe
sumber
4

Saya menggunakan ini:

@var.respond_to?(:keys)

Ini berfungsi untuk Hash dan ActiveSupport :: HashWithIndifferentAccess.

drinor
sumber
4

Dalam praktiknya, Anda akan sering ingin bertindak berbeda tergantung pada apakah variabel itu Array atau Hash, bukan hanya sekadar memberi tahu. Dalam situasi ini, idiom elegan adalah sebagai berikut:

case item
  when Array
   #do something
  when Hash
   #do something else
end

Perhatikan bahwa Anda tidak mengaktifkan .classmetode ini item.

Daniel Szmulewicz
sumber
2

Kamu bisa memakai instance_of?

misalnya

@some_var.instance_of?(Hash)
Pisau
sumber
Perhatikan bahwa ini tidak berfungsi untuk subclass ! Misalnya, jika Anda memiliki ActiveRecord::Collectiondan mencoba instance_of?(Array)maka ini akan kembali false, sedangkan is_a?(Array)akan kembali true.
Tom Lord
0

Jika Anda ingin menguji apakah suatu objek secara ketat atau meluas Hash, gunakan:

value = {}
value.is_a?(Hash) || value.is_a?(Array) #=> true

Tetapi untuk membuat nilai dari mengetik bebek Ruby, Anda bisa melakukan sesuatu seperti:

value = {}
value.respond_to?(:[]) #=> true

Ini berguna ketika Anda hanya ingin mengakses beberapa nilai menggunakan value[:key]sintaks.

Harap dicatat bahwa Array.new["key"]akan memunculkan a TypeError.

Vinicius Brasil
sumber
-1
irb(main):005:0> {}.class
=> Hash
irb(main):006:0> [].class
=> Array
Spyros
sumber