Một cách thanh lịch trong Ruby để biết một biến là Hash hay Array là gì?


140

Để kiểm tra xem @some_vartôi đang làm gì

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

Tôi chắc chắn có một cách thanh lịch hơn để kiểm tra nếu @some_varlà một Hashhoặc một Array.


1
Andrew: Tôi đang gọi một API và trong JSON tôi nhận lại, nếu có nhiều kết quả tôi nhận được một Mảng nhưng nếu chỉ có một thì tôi nhận được một Hash chứ không phải là một mảng phần tử. Có một chuyển tiếp tốt hơn so với thực hiện kiểm tra Array vs Hash?
mất nước

2
Vì vậy, bạn nhận được một trong hai [result_hash, another_result_hash]hoặc single_result_hash? Bất cứ ai tạo ra API đó đều không làm tốt công việc!
Andrew Grimm

2
Bạn nói đúng, Andrew, và tôi đặt cược đó là rất nhiều dễ dàng hơn để có được những người đã viết API để sửa chữa nó hơn để thử nghiệm cho một hash so với một mảng.
Olivier 'Ölbaum' Scherler

tôi có cùng hoàn cảnh với @drhyde. Trong trường hợp của tôi, bên thứ 3 là HTTParty, sau khi phân tích tệp XML, không có cách nào để quyết định đâu là cách tốt nhất để xử lý tình huống trong đó một phần tử có thể có con 1-n.
Bẩn Henry

Bạn nên xem Avdi Grimms screencast: rubytapas.com/2016/10/17/epiT-451-advified- class-membership và có thể làm cho cabos trả lời được chấp nhận.
Alexander Presber

Câu trả lời:


261

Bạn chỉ có thể làm:

@some_var.class == Hash

hoặc cũng có thể như:

@some_var.is_a?(Hash)

Đáng chú ý là "is_a?" phương thức là đúng nếu lớp là bất cứ nơi nào trong cây tổ tiên của đối tượng. ví dụ:

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

điều trên là đúng nếu @some_var là một thể hiện của hàm băm hoặc lớp khác bắt nguồn từ Object. Vì vậy, nếu bạn muốn một trận đấu nghiêm ngặt về loại lớp, sử dụng == hoặc instance_of? phương pháp có lẽ là những gì bạn đang tìm kiếm.


20
is_a?là tùy chọn tốt nhất, vì nó cũng trả về truecho các lớp con.
Fábio Batista

Và, tất nhiên, bạn cũng bỏ dấu ngoặc, vì vậy @som_var.is_a? Hashcũng hoạt động :)
Gus Shortz

7
Hãy cẩn thận, điều này thực sự có thể làm phiền bạn nếu ai đó kết thúc với bạn một phiên bản ActiveSupport :: HashWithIndifferentAccess. Phản hồi giống như hàm băm, nhưng thực tế không phải là Hash. :(
unflores

Tôi cũng muốn có nhiều câu trả lời chi tiết hơn về việc gõ vịt, vì tác giả ban đầu rõ ràng đang tìm kiếm một cách thức ruby ​​để thực hiện kiểm tra loại.
NobodysNightmare

3
@unflores ActiveSupport::HashWithIndifferentAccessthừa hưởng từ Hash, do đó @some_var.is_a?(Hash)cũng sẽ trả về true trong trường hợp này. (Tôi chỉ có cùng một câu hỏi và trường hợp HashWithInifferentAccess và có một chút bối rối từ nhận xét của bạn ... vì vậy tôi muốn làm rõ)
Stilzk1n

38

Trước hết, câu trả lời tốt nhất cho câu hỏi theo nghĩa đen là

Hash === @some_var

Nhưng câu hỏi thực sự cần được trả lời bằng cách chỉ ra cách gõ vịt ở đây. Điều đó phụ thuộc một chút vào loại vịt bạn cần.

@some_var.respond_to?(:each_pair)

hoặc là

@some_var.respond_to?(:has_key?)

hoặc thậm chí

@some_var.respond_to?(:to_hash)

có thể đúng tùy thuộc vào ứng dụng.


25

Thông thường trong ruby ​​khi bạn đang tìm kiếm "loại" bạn thực sự muốn "loại vịt" hoặc "không giống như một con vịt?". Bạn sẽ thấy nếu nó đáp ứng với một phương thức nhất định:

@some_var.respond_to?(:each)

Bạn có thể lặp lại qua @some_var vì nó phản hồi lại: mỗi

Nếu bạn thực sự muốn biết loại và nếu đó là Hash hoặc Array thì bạn có thể làm:

["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

Điều này sẽ không hoạt động nếu mã của bạn phụ thuộc vào thứ tự của dữ liệu (ví dụ: nếu bạn đang sử dụng Each_with_index). Thứ tự của các yếu tố được thực hiện khác nhau giữa băm và mảng và nó là khác nhau giữa các phiên bản ruby (. Intertwingly.net/slides/2008/oscon/ruby19/22 )
juan2raid

1
@ juan2ston: Nếu đơn hàng là quan trọng, thì hãy gọi sortcho nó trước.
Andrew Grimm

Ví dụ thứ hai @Brandon nên chuyển đổi lớp thành chuỗi. <br/>["Hash", "Array"].include?(@some_var.class.to_s) #=> check both through instance class
OBCENEIKON

12
Hash === @some_var #=> return Boolean

điều này cũng có thể được sử dụng với tuyên bố trường hợp

case @some_var
when Hash
   ...
when Array
   ...
end

4

Tôi sử dụng cái này:

@var.respond_to?(:keys)

Nó hoạt động cho Hash và ActiveSupport :: HashWithIndifferentAccess.


4

Trong thực tế, bạn thường sẽ muốn hành động khác nhau tùy thuộc vào việc một biến là Mảng hay Hash, không chỉ đơn thuần là nói. Trong tình huống này, một thành ngữ thanh lịch là như sau:

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

Lưu ý rằng bạn không gọi .classphương thức trên item.


2

Bạn có thể dùng instance_of?

ví dụ

@some_var.instance_of?(Hash)

Lưu ý rằng điều này không hoạt động cho các lớp con ! Ví dụ, nếu bạn có một ActiveRecord::Collectionvà thử instance_of?(Array)thì điều này sẽ trở lại false, trong khi is_a?(Array)sẽ trở lại true.
Tom Lord

0

Nếu bạn muốn kiểm tra xem một đối tượng có nghiêm ngặt hoặc mở rộng a hay không Hash, hãy sử dụng:

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

Nhưng để tạo ra giá trị của việc gõ vịt của Ruby, bạn có thể làm một cái gì đó như:

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

Nó rất hữu ích khi bạn chỉ muốn truy cập một số giá trị bằng value[:key]cú pháp.

Xin lưu ý rằng Array.new["key"]sẽ tăng a TypeError.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.