174

To check what @some_var is, I am doing a

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

I am sure that there is a more elegant way to check if @some_var is a Hash or an Array.

stamat
  • 1,832
  • 21
  • 26
drhyde
  • 1,851
  • 2
  • 11
  • 6
  • 1
    Andrew: I am calling an API and in the JSON I get back, if there are multiple results I get an Array but if there is only one I get a Hash rather than a single element Array. Is there a better forward than doing the Array vs Hash check? – drhyde Mar 22 '11 at 05:01
  • 2
    So you get either `[result_hash, another_result_hash]` or `single_result_hash`? Whoever created that API wasn't doing a good job! – Andrew Grimm Mar 22 '11 at 05:38
  • 2
    You’re right, Andrew, and I bet it’s *a lot* easier to get the people who wrote the API to fix it than to test for a hash versus an array. – Olivier 'Ölbaum' Scherler May 24 '12 at 21:28
  • i have the same situation as @drhyde. In my case the 3rd party is HTTParty which after parsing an XML file has no way to decide what is the best way to handle a situation where an element can have 1-n children. – Mick F Feb 06 '13 at 15:44
  • You should watch Avdi Grimms screencast: https://www.rubytapas.com/2016/10/17/episode-451-advanced-class-membership and probably make cabos answer the accepted one. – Alexander Presber Oct 19 '16 at 11:55

9 Answers9

308

You can just do:

@some_var.class == Hash

or also something like:

@some_var.is_a?(Hash)

It's worth noting that the "is_a?" method is true if the class is anywhere in the objects ancestry tree. for instance:

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

the above is true if @some_var is an instance of a hash or other class that stems from Object. So, if you want a strict match on the class type, using the == or instance_of? method is probably what you're looking for.

Pete
  • 17,885
  • 4
  • 32
  • 30
  • 21
    `is_a?` is the best option, since it also returns `true` for subclasses. – Fábio Batista Mar 20 '11 at 06:32
  • And, of course, you also leave out the brackets, so `@som_var.is_a? Hash` works too :) – Gus Shortz Mar 03 '13 at 03:51
  • 7
    Be careful, this could really screw you if someone ends up passing you an instance of ActiveSupport::HashWithIndifferentAccess. Which responds like a hash, but is not in fact a Hash. :( – unflores Mar 06 '14 at 14:11
  • I'd also have liked more answers going into detail regarding duck-typing, since the original author apparently was looking for a ruby way of doing a type check. – NobodysNightmare Aug 25 '15 at 11:55
  • 3
    @unflores `ActiveSupport::HashWithIndifferentAccess` inherits from Hash, hence `@some_var.is_a?(Hash)` will return true in this case as well. (I just had the same question and HashWithIndifferentAccess case and got a bit confused from your comment...so I wanted to clarify) – Stilzk1n Jul 08 '16 at 11:57
  • @NobodysNightmare - I don't know if there's a 'Ruby way' of doing a type check. You should strive to eliminate type checks in object-oriented languages, I've been learning. External APIs are definitely tricky. – phillyslick Aug 27 '16 at 02:12
  • I've also had issues with using this method in `case` statments. Seemed to only work for me in `if..else` statements. – FilBot3 Aug 04 '17 at 15:22
  • Rubocop seems to prefer `instance_of?(Hash)` – mtrussell Jun 01 '21 at 18:50
46

First of all, the best answer for the literal question is

Hash === @some_var

But the question really should have been answered by showing how to do duck-typing here. That depends a bit on what kind of duck you need.

@some_var.respond_to?(:each_pair)

or

@some_var.respond_to?(:has_key?)

or even

@some_var.respond_to?(:to_hash)

may be right depending on the application.

cabo
  • 987
  • 8
  • 9
26

Usually in ruby when you are looking for "type" you are actually wanting the "duck-type" or "does is quack like a duck?". You would see if it responds to a certain method:

@some_var.respond_to?(:each)

You can iterate over @some_var because it responds to :each

If you really want to know the type and if it is Hash or Array then you can do:

["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
  • 2,574
  • 19
  • 17
  • This won't work if your code is dependent on the ordering of the data(e.g. if you are using each_with_index). The order of the elements is implemented differently between hashes and arrays and it is different between ruby versions.(http://intertwingly.net/slides/2008/oscon/ruby19/22) – juan2raid Mar 20 '11 at 18:28
  • 1
    @juan2raid: If order is important, then call `sort` on it first. – Andrew Grimm Mar 20 '11 at 22:33
  • @Brandon second example should convert the class to string.
    `["Hash", "Array"].include?(@some_var.class.to_s) #=> check both through instance class`
    – OBCENEIKON Jun 05 '15 at 16:27
14
Hash === @some_var #=> return Boolean

this can also be used with case statement

case @some_var
when Hash
   ...
when Array
   ...
end
GutenYe
  • 3,227
  • 2
  • 25
  • 21
4

I use this:

@var.respond_to?(:keys)

It works for Hash and ActiveSupport::HashWithIndifferentAccess.

rjurado01
  • 5,337
  • 3
  • 36
  • 45
4

In practice, you will often want to act differently depending on whether a variable is an Array or a Hash, not just mere tell. In this situation, an elegant idiom is the following:

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

Note that you don't call the .class method on item.

Daniel Szmulewicz
  • 3,971
  • 2
  • 25
  • 26
4

You can use instance_of?

e.g

@some_var.instance_of?(Hash)
Shiv
  • 8,362
  • 5
  • 29
  • 32
  • Note that this does not work for *subclasses*! For example, if you have an `ActiveRecord::Collection` and try `instance_of?(Array)` then this will return `false`, whereas `is_a?(Array)` will return `true`. – Tom Lord Jul 15 '16 at 10:06
1

If you want to test if an object is strictly or extends a Hash, use:

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

But to make value of Ruby's duck typing, you could do something like:

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

It is useful when you only want to access some value using the value[:key] syntax.

Please note that Array.new["key"] will raise a TypeError.

vinibrsl
  • 6,563
  • 4
  • 31
  • 44
-2
irb(main):005:0> {}.class
=> Hash
irb(main):006:0> [].class
=> Array
Spyros
  • 46,820
  • 25
  • 86
  • 129