¿Cuál es una manera elegante en Ruby para saber si una variable es un Hash o una matriz?

141

Para comprobar qué @some_vares, estoy haciendo un

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

Estoy seguro de que hay una forma más elegante de verificar si @some_vares una Hasho una Array.

drhyde
fuente
1
Andrew: Estoy llamando a una API y en el JSON vuelvo, si hay varios resultados, obtengo una matriz, pero si solo hay una, obtengo un hash en lugar de una matriz de un solo elemento. ¿Hay un mejor avance que hacer la verificación Array vs Hash?
drhyde
2
¿Entonces obtienes uno [result_hash, another_result_hash]o single_result_hash? ¡Quien haya creado esa API no estaba haciendo un buen trabajo!
Andrew Grimm
2
Tienes razón, Andrew, y apuesto a que es mucho más fácil conseguir que las personas que escribieron la API lo arreglen que probar un hash frente a una matriz.
Olivier 'Ölbaum' Scherler
Tengo la misma situación que @drhyde. En mi caso, el tercero es HTTParty, que después de analizar un archivo XML no tiene forma de decidir cuál es la mejor manera de manejar una situación en la que un elemento puede tener 1 n hijos.
Dirty Henry
Debería ver el screencast de Avdi Grimms: rubytapas.com/2016/10/17/episode-451-advanced-class-membership y probablemente hacer que el caos responda al aceptado.
Alexander Presber

Respuestas:

263

Solo puedes hacer:

@some_var.class == Hash

o también algo como:

@some_var.is_a?(Hash)

Vale la pena señalar que el "is_a?" El método es verdadero si la clase está en cualquier parte del árbol de ascendencia de los objetos. por ejemplo:

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

lo anterior es cierto si @some_var es una instancia de un hash u otra clase que se deriva de Object. Entonces, si desea una coincidencia estricta en el tipo de clase, utilizando == o instancia_de? El método es probablemente lo que estás buscando.

Pete
fuente
20
is_a?es la mejor opción, ya que también regresa truepara subclases.
Fábio Batista
Y, por supuesto, también omites los corchetes, así que @som_var.is_a? Hashtambién funciona :)
Gus Shortz
77
Tenga cuidado, esto realmente podría fastidiarlo si alguien termina pasándole una instancia de ActiveSupport :: HashWithIndifferentAccess. Lo que responde como un hash, pero en realidad no es un Hash. :(
despliega
También me hubieran gustado más respuestas que entraran en detalles con respecto a la escritura de patos, ya que el autor original aparentemente estaba buscando una forma rubí de hacer una verificación de tipo.
NobodysNightmare
3
@unflores ActiveSupport::HashWithIndifferentAccesshereda de Hash, por @some_var.is_a?(Hash)lo tanto, también será cierto en este caso. (Solo tenía la misma pregunta y el caso HashWithIndifferentAccess y me confundí un poco por su comentario ... así que quería aclarar)
Stilzk1n
38

En primer lugar, la mejor respuesta para la pregunta literal es

Hash === @some_var

Pero la pregunta realmente debería haberse respondido mostrando cómo hacer tipear patos aquí. Eso depende un poco del tipo de pato que necesites.

@some_var.respond_to?(:each_pair)

o

@some_var.respond_to?(:has_key?)

o incluso

@some_var.respond_to?(:to_hash)

puede ser correcto dependiendo de la aplicación.

Cabo
fuente
25

Por lo general, en rubí cuando buscas "tipo", en realidad quieres el "tipo pato" o "¿grazna como un pato?". Vería si responde a cierto método:

@some_var.respond_to?(:each)

Puede iterar sobre @some_var porque responde a: cada

Si realmente quiere saber el tipo y si es Hash o Array, puede hacer lo siguiente:

["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
fuente
Esto no funcionará si su código depende del orden de los datos (por ejemplo, si está usando each_with_index). El orden de los elementos es implementado de manera diferente entre hashes y arrays y es diferente entre las versiones de rubí (. Intertwingly.net/slides/2008/oscon/ruby19/22 )
juan2raid
1
@ juan2raid: Si el orden es importante, sortprimero instálelo .
Andrew Grimm
El segundo ejemplo de @Brandon debería convertir la clase a cadena. <br/>["Hash", "Array"].include?(@some_var.class.to_s) #=> check both through instance class
OBCENEIKON
12
Hash === @some_var #=> return Boolean

esto también se puede usar con declaración de caso

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

Yo uso esto:

@var.respond_to?(:keys)

Funciona para Hash y ActiveSupport :: HashWithIndifferentAccess.

drinor
fuente
4

En la práctica, a menudo querrá actuar de manera diferente dependiendo de si una variable es una matriz o un hash, no solo una simple indicación. En esta situación, un idioma elegante es el siguiente:

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

Tenga en cuenta que no llama al .classmétodo item.

Daniel Szmulewicz
fuente
2

Puedes usar instance_of?

p.ej

@some_var.instance_of?(Hash)
Shiv
fuente
¡Tenga en cuenta que esto no funciona para las subclases ! Por ejemplo, si tiene un ActiveRecord::Collectione intente instance_of?(Array), esto volverá false, mientras is_a?(Array)que volverá true.
Tom Lord
0

Si desea probar si un objeto es estrictamente o extiende a Hash, use:

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

Pero para valorar la escritura del pato de Ruby, podría hacer algo como:

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

Es útil cuando solo desea acceder a algún valor utilizando la value[:key]sintaxis.

Tenga en cuenta que Array.new["key"]elevará a TypeError.

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