Was ist eine elegante Methode in Ruby, um festzustellen, ob eine Variable ein Hash oder ein Array ist?

140

Um zu überprüfen, was @some_varist, mache ich eine

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

Ich bin sicher, es gibt eine elegantere Möglichkeit, um zu überprüfen, ob @some_vares sich um eine Hashoder eine handelt Array.

Drhyd
quelle
1
Andrew: Ich rufe eine API auf und im JSON bekomme ich zurück, wenn es mehrere Ergebnisse gibt, bekomme ich ein Array, aber wenn es nur eines gibt, bekomme ich einen Hash anstatt eines einzelnen Element-Arrays. Gibt es einen besseren Forward als die Array vs Hash-Prüfung?
Drhyde
2
Also bekommst du entweder [result_hash, another_result_hash]oder single_result_hash? Wer auch immer diese API erstellt hat, hat keine gute Arbeit geleistet!
Andrew Grimm
2
Sie haben Recht, Andrew, und ich wette, es ist viel einfacher, die Leute, die die API geschrieben haben, dazu zu bringen, sie zu reparieren, als auf einen Hash im Vergleich zu einem Array zu testen.
Olivier 'Ölbaum' Scherler
Ich habe die gleiche Situation wie @drhyde. In meinem Fall ist der Drittanbieter HTTParty, der nach dem Parsen einer XML-Datei nicht entscheiden kann, wie am besten mit einer Situation umgegangen werden kann, in der ein Element 1-n Kinder haben kann.
Schmutziger Henry
Sie sollten sich den Screencast von Avdi Grimms ansehen: rubytapas.com/2016/10/17/episode-451-advanced-class-membership und wahrscheinlich Cabos dazu bringen, die akzeptierte zu beantworten.
Alexander Presber

Antworten:

261

Sie können einfach tun:

@some_var.class == Hash

oder auch so etwas wie:

@some_var.is_a?(Hash)

Es ist erwähnenswert, dass das "is_a?" Die Methode ist wahr, wenn sich die Klasse an einer beliebigen Stelle im Stammbaum der Objekte befindet. zum Beispiel:

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

Dies gilt, wenn @some_var eine Instanz eines Hashs oder einer anderen Klasse ist, die von Object stammt. Wenn Sie also eine strikte Übereinstimmung mit dem Klassentyp wünschen, verwenden Sie == oder instance_of? Methode ist wahrscheinlich das, wonach Sie suchen.

Pete
quelle
20
is_a?ist die beste Option, da sie auch truefür Unterklassen zurückgegeben wird.
Fábio Batista
Und natürlich lässt du auch die Klammern weg, @som_var.is_a? Hashfunktioniert also auch :)
Gus Shortz
7
Seien Sie vorsichtig, dies könnte Sie wirklich verarschen, wenn Ihnen jemand eine Instanz von ActiveSupport :: HashWithIndifferentAccess übergibt. Was wie ein Hash reagiert, aber eigentlich kein Hash ist. :(
Unflores
Ich hätte mir auch mehr detaillierte Antworten zum Thema Ententypisierung gewünscht, da der ursprüngliche Autor anscheinend nach einer rubinroten Methode zur Typprüfung suchte.
NobodysNightmare
3
@unflores ActiveSupport::HashWithIndifferentAccesserbt von Hash und gibt daher @some_var.is_a?(Hash)auch in diesem Fall true zurück. (Ich hatte gerade die gleiche Frage und HashWithIndifferentAccess-Fall und wurde ein wenig verwirrt von Ihrem Kommentar ... also wollte ich klarstellen)
Stilzk1n
38

Zuallererst ist die beste Antwort auf die wörtliche Frage

Hash === @some_var

Aber die Frage hätte wirklich beantwortet werden müssen, indem gezeigt wurde, wie man hier Enten tippt. Das hängt ein bisschen davon ab, welche Art von Ente du brauchst.

@some_var.respond_to?(:each_pair)

oder

@some_var.respond_to?(:has_key?)

oder auch

@some_var.respond_to?(:to_hash)

kann je nach Anwendung richtig sein.

cabo
quelle
25

Normalerweise wollen Sie in Ruby, wenn Sie nach "Typ" suchen, tatsächlich den "Ententyp" oder "Ist Quacksalber wie eine Ente?". Sie würden sehen, ob es auf eine bestimmte Methode reagiert:

@some_var.respond_to?(:each)

Sie können über @some_var iterieren, da es auf Folgendes reagiert:

Wenn Sie den Typ wirklich kennen möchten und es sich um Hash oder Array handelt, können Sie Folgendes tun:

["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
quelle
Dies funktioniert nicht, wenn Ihr Code von der Reihenfolge der Daten abhängt (z. B. wenn Sie each_with_index verwenden). Die Reihenfolge der Elemente ist unterschiedlich zwischen Hashes und Arrays implementiert , und es ist unterschiedlich zwischen rubin Versionen (. Intertwingly.net/slides/2008/oscon/ruby19/22 )
juan2raid
1
@ juan2raid: Wenn die Reihenfolge wichtig ist, rufen Sie sortsie zuerst an.
Andrew Grimm
@Brandon zweites Beispiel sollte die Klasse in Zeichenfolge konvertieren. <br/>["Hash", "Array"].include?(@some_var.class.to_s) #=> check both through instance class
OBCENEIKON
12
Hash === @some_var #=> return Boolean

Dies kann auch mit case-Anweisungen verwendet werden

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

Ich benutze das:

@var.respond_to?(:keys)

Es funktioniert für Hash und ActiveSupport :: HashWithIndifferentAccess.

Drinor
quelle
4

In der Praxis möchten Sie häufig anders handeln, je nachdem, ob eine Variable ein Array oder ein Hash ist, und nicht nur sagen. In dieser Situation ist eine elegante Redewendung die folgende:

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

Beachten Sie, dass Sie die .classMethode nicht aufrufen item.

Daniel Szmulewicz
quelle
2

Sie können verwenden instance_of?

z.B

@some_var.instance_of?(Hash)
Shiv
quelle
Beachten Sie, dass dies für Unterklassen nicht funktioniert ! Wenn Sie beispielsweise ein ActiveRecord::Collectionund versuchen, instance_of?(Array)wird dieses zurückgegeben false, während es zurückgegeben is_a?(Array)wird true.
Tom Lord
0

Wenn Sie testen möchten, ob ein Objekt streng ist oder a erweitert Hash, verwenden Sie:

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

Aber um Rubys Enten-Tippen wertzuschätzen, könnten Sie Folgendes tun:

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

Dies ist nützlich, wenn Sie nur über die value[:key]Syntax auf einen bestimmten Wert zugreifen möchten .

Bitte beachten Sie, dass Array.new["key"]a TypeError.

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