Quelle est la manière élégante dans Ruby de dire si une variable est un hachage ou un tableau?

141

Pour vérifier ce que @some_varc'est, je fais un

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

Je suis sûr qu'il existe un moyen plus élégant de vérifier si @some_varest un Hashou un Array.

drhyde
la source
1
Andrew: J'appelle une API et dans le JSON je reviens, s'il y a plusieurs résultats, j'obtiens un tableau, mais s'il n'y en a qu'un, j'obtiens un hachage plutôt qu'un seul élément. Y a-t-il un meilleur avant que de faire la vérification Array vs Hash?
drhyde
2
Alors vous obtenez soit [result_hash, another_result_hash]ou single_result_hash? Celui qui a créé cette API ne faisait pas du bon travail!
Andrew Grimm
2
Vous avez raison, Andrew, et je parie qu'il est beaucoup plus facile d'amener les gens qui ont écrit l'API à le réparer que de tester un hachage par rapport à un tableau.
Olivier 'Ölbaum' Scherler
j'ai la même situation que @drhyde. Dans mon cas, le tiers est HTTParty qui, après avoir analysé un fichier XML, n'a aucun moyen de décider quelle est la meilleure façon de gérer une situation où un élément peut avoir 1-n enfants.
Dirty Henry
Vous devriez regarder le screencast d'Avdi Grimms: rubytapas.com/2016/10/17/episode-451-advanced-class-membership et probablement faire en sorte que les cabos répondent à l'acceptation.
Alexander Presber

Réponses:

263

Vous pouvez simplement faire:

@some_var.class == Hash

ou aussi quelque chose comme:

@some_var.is_a?(Hash)

Il convient de noter que le "is_a?" La méthode est vraie si la classe est n'importe où dans l'arborescence d'ascendance des objets. par exemple:

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

ce qui précède est vrai si @some_var est une instance d'un hachage ou d'une autre classe qui provient d'Object. Donc, si vous voulez une correspondance stricte sur le type de classe, utilisez le == ou instance_of? La méthode est probablement ce que vous recherchez.

Pete
la source
20
is_a?est la meilleure option, car elle renvoie également truepour les sous-classes.
Fábio Batista
Et, bien sûr, vous laissez également les parenthèses, ça @som_var.is_a? Hashmarche aussi :)
Gus Shortz
7
Attention, cela pourrait vraiment vous déranger si quelqu'un finit par vous passer une instance d'ActiveSupport :: HashWithIndifferentAccess. Qui répond comme un hachage, mais n'est en fait pas un hachage. :(
unflores
J'aurais également aimé plus de réponses détaillées concernant la saisie de canard, puisque l'auteur original cherchait apparemment une manière rubis de faire une vérification de type.
NobodysNightmare
3
@unflores ActiveSupport::HashWithIndifferentAccesshérite de Hash, donc @some_var.is_a?(Hash)retournera true dans ce cas également. (Je viens d'avoir la même question et le cas HashWithIndifferentAccess et j'ai été un peu confus à cause de votre commentaire ... alors je voulais clarifier)
Stilzk1n
38

Tout d'abord, la meilleure réponse à la question littérale est

Hash === @some_var

Mais la question aurait vraiment dû être répondue en montrant comment faire du typage canard ici. Cela dépend un peu du type de canard dont vous avez besoin.

@some_var.respond_to?(:each_pair)

ou

@some_var.respond_to?(:has_key?)

ou même

@some_var.respond_to?(:to_hash)

peut avoir raison selon l'application.

cabo
la source
25

Habituellement, en rubis, lorsque vous recherchez «type», vous voulez en fait le «type canard» ou «est-ce que charlatan est comme un canard? Vous verriez s'il répond à une certaine méthode:

@some_var.respond_to?(:each)

Vous pouvez parcourir @some_var car il répond à: each

Si vous voulez vraiment connaître le type et s'il s'agit de Hash ou Array, vous pouvez faire:

["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
la source
Cela ne fonctionnera pas si votre code dépend de l'ordre des données (par exemple, si vous utilisez each_with_index). L'ordre des éléments est mis en œuvre différemment entre les hash et les tableaux et il est différent entre les versions de rubis (. Intertwingly.net/slides/2008/oscon/ruby19/22 )
juan2raid
1
@ juan2raid: Si l'ordre est important, appelez- sortle d'abord.
Andrew Grimm
@Brandon deuxième exemple devrait convertir la classe en chaîne. <br/>["Hash", "Array"].include?(@some_var.class.to_s) #=> check both through instance class
OBCENEIKON
12
Hash === @some_var #=> return Boolean

cela peut également être utilisé avec l'instruction case

case @some_var
when Hash
   ...
when Array
   ...
end
GutenYe
la source
4

J'utilise ceci:

@var.respond_to?(:keys)

Cela fonctionne pour Hash et ActiveSupport :: HashWithIndifferentAccess.

drinor
la source
4

En pratique, vous voudrez souvent agir différemment selon que la variable est un tableau ou un hachage, et pas seulement un simple tell. Dans cette situation, un idiome élégant est le suivant:

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

Notez que vous n'appelez pas la .classméthode item.

Daniel Szmulewicz
la source
2

Vous pouvez utiliser instance_of?

par exemple

@some_var.instance_of?(Hash)
Shiv
la source
Notez que cela ne fonctionne pas pour les sous-classes ! Par exemple, si vous avez un ActiveRecord::Collectionet essayez, instance_of?(Array)cela reviendra false, alors que vous is_a?(Array)reviendrez true.
Tom Lord
0

Si vous voulez tester si un objet est strictement ou étend a Hash, utilisez:

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

Mais pour valoriser le typage du canard de Ruby, vous pouvez faire quelque chose comme:

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

C'est utile lorsque vous ne souhaitez accéder qu'à une valeur en utilisant la value[:key]syntaxe.

Veuillez noter que Array.new["key"]cela augmentera un TypeError.

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