在Ruby中,有什么优雅的方法可以判断变量是哈希还是数组?


140

为了检查是什么@some_var,我正在做一个

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

我敢肯定有一种更优雅的方法来检查@some_vara Hash或an Array


1
安德鲁:我正在调用一个API,然后返回JSON,如果有多个结果,我得到一个数组,但是如果只有一个,我得到一个哈希,而不是单个元素数组。是否有比进行Array vs Hash检查更好的选择?
drhyde 2011年

2
所以,你要么[result_hash, another_result_hash]还是single_result_hash?创建该API的人做得不好!
安德鲁·格林

2
你说得对,安德鲁,我敢打赌这是很多更容易得到谁写的API来解决它,而不是测试的哈希与数组中的人。
Olivier'Ölbaum'Scherler '12

我和@drhyde的情况一样。在我的情况下,第3方是HTTParty,它在解析XML文件后无法决定什么是处理元素可以具有1-n个子元素的最佳方式。
肮脏的亨利

您应该观看Avdi Grimms的截屏视频:rubytapas.com/2016/10/17/episode-451-advanced-class-membership,并且可能使专家组回答被接受的问题。
亚历山大·普雷斯伯

Answers:


261

您可以这样做:

@some_var.class == Hash

或类似的东西:

@some_var.is_a?(Hash)

值得注意的是“ is_a?” 如果该类在对象祖先树中的任何位置,则方法为true。例如:

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

如果@some_var是源自Object的哈希或其他类的实例,则以上内容适用。因此,如果要对类类型进行严格匹配,请使用==或instance_of?方法可能是您要寻找的。


20
is_a?是最佳选择,因为它还会返回true子类。
法比奥·巴蒂斯塔

而且,当然,您也省略了方括号,因此@som_var.is_a? Hash也可以使用:)
Gus Shortz

7
请注意,如果有人最终向您传递了ActiveSupport :: HashWithIndifferentAccess实例,这可能会给您带来麻烦。它像哈希一样响应,但实际上不是哈希。:(
unflores 2014年

我还希望有更多有关鸭类输入的详细答案,因为原始作者显然正在寻找一种进行类型检查的红宝石方法。
没人噩梦2015年

3
@unflores ActiveSupport::HashWithIndifferentAccess继承自Hash,因此@some_var.is_a?(Hash)在这种情况下也将返回true。(我只是有相同的问题和HashWithIndifferentAccess案例,并从您的评论中有点困惑...所以我想澄清
一下

38

首先,字面问题的最佳答案是

Hash === @some_var

但是,实际上应该通过在此处显示如何进行鸭式打字来回答这个问题。这取决于您需要哪种鸭子。

@some_var.respond_to?(:each_pair)

要么

@some_var.respond_to?(:has_key?)

甚至

@some_var.respond_to?(:to_hash)

可能是正确的,具体取决于应用程序。


25

通常在红宝石中,当您寻找“类型”时,您实际上是在想要“鸭子类型”或“像鸭子一样嘎嘎吗?”。您将看到它是否响应某种方法:

@some_var.respond_to?(:each)

您可以遍历@some_var,因为它会响应:each

如果您真的想知道类型,并且它是哈希或数组,则可以执行以下操作:

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

如果您的代码依赖于数据的顺序(例如,如果您正在使用each_with_index),则此方法将无效。的元素的顺序被散列和阵列之间不同的方式实现,这是红宝石版本之间不同(intertwingly.net/slides/2008/oscon/ruby19/22
juan2raid

1
@ juan2raid:如果订单很重要,sort请先致电。
Andrew Grimm'3

@Brandon第二个示例应将类转换为字符串。<br/>["Hash", "Array"].include?(@some_var.class.to_s) #=> check both through instance class
OBCENEIKON 2015年

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

这也可以与case语句一起使用

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

4

我用这个:

@var.respond_to?(:keys)

它适用于Hash和ActiveSupport :: HashWithIndifferentAccess。


4

在实践中,您常常希望根据变量是数组还是哈希而不是仅仅告诉变量来采取不同的行动。在这种情况下,一个优雅的习惯用法如下:

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

请注意,不要在.class上调用方法item


2

您可以使用 instance_of?

例如

@some_var.instance_of?(Hash)

请注意,这不适用于子类!例如,如果您有一个ActiveRecord::Collectionand尝试,instance_of?(Array)那么它将返回false,而is_a?(Array)将返回true
汤姆·罗德

0

如果要测试某个对象是严格还是扩展了Hash,请使用:

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

但是要使Ruby的鸭子打字更有价值,您可以执行以下操作:

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

当您只想使用value[:key]语法访问某些值时,此功能很有用。

请注意,这Array.new["key"]会引发TypeError


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.