Qual è un modo elegante in Ruby per dire se una variabile è un hash o una matrice?


141

Per controllare cos'è @some_var, sto facendo un

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

Sono sicuro che esiste un modo più elegante per verificare se @some_varè un Hasho un Array.


1
Andrew: sto chiamando un'API e nel JSON torno indietro, se ci sono più risultati ottengo un array ma se ne esiste solo uno ottengo un hash anziché un array di singoli elementi. C'è un vantaggio migliore rispetto al controllo Array vs Hash?
drhyde,

2
Quindi ottieni uno [result_hash, another_result_hash]o single_result_hash? Chiunque abbia creato quell'API non stava facendo un buon lavoro!
Andrew Grimm,

2
Hai ragione, Andrew, e scommetto che è molto più facile convincere le persone che hanno scritto l'API a risolverlo piuttosto che testare un hash contro un array.
Olivier "Ölbaum" Scherler,

ho la stessa situazione di @drhyde. Nel mio caso la terza parte è HTTParty che dopo aver analizzato un file XML non ha modo di decidere quale sia il modo migliore per gestire una situazione in cui un elemento può avere 1-n figli.
Dirty Henry,

Dovresti guardare Avdi Grimms screencast: rubytapas.com/2016/10/17/episode-451-advanced-class-membership e probabilmente far rispondere i cabos a quello accettato.
Alexander Presber,

Risposte:


263

Puoi semplicemente fare:

@some_var.class == Hash

o anche qualcosa di simile:

@some_var.is_a?(Hash)

Vale la pena notare che "is_a?" Il metodo è vero se la classe si trova in qualsiasi punto dell'albero degli antenati degli oggetti. per esempio:

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

quanto sopra è vero se @some_var è un'istanza di un hash o di un'altra classe che deriva da Object. Quindi, se vuoi una corrispondenza rigorosa sul tipo di classe, usando == o istanza_di? il metodo è probabilmente quello che stai cercando.


20
is_a?è l'opzione migliore, poiché restituisce anche trueper le sottoclassi.
Fábio Batista,

E, naturalmente, tralasciamo anche le parentesi, quindi @som_var.is_a? Hashfunziona anche :)
Gus Shortz

7
Fai attenzione, questo potrebbe davvero rovinarti se qualcuno finisce per inviarti un'istanza di ActiveSupport :: HashWithIndifferentAccess. Che risponde come un hash, ma in realtà non è un hash. :(
chiarisce il

Mi sarebbero anche piaciute più risposte approfondendo la tipizzazione delle anatre, poiché l'autore originale era apparentemente alla ricerca di un modo rubino di fare un controllo del tipo.
NobodysNightmare,

3
@unflores ActiveSupport::HashWithIndifferentAccesseredita da Hash, quindi @some_var.is_a?(Hash)tornerà vero anche in questo caso. (Ho appena avuto la stessa domanda e il caso HashWithIndifferentAccess e sono rimasto un po 'confuso dal tuo commento ... quindi volevo chiarire)
Stilzk1n

38

Prima di tutto, la migliore risposta per la domanda letterale è

Hash === @some_var

Ma alla domanda avrebbe davvero dovuto dare una risposta mostrando come eseguire la digitazione delle anatre qui. Dipende un po 'dal tipo di anatra di cui hai bisogno.

@some_var.respond_to?(:each_pair)

o

@some_var.respond_to?(:has_key?)

o anche

@some_var.respond_to?(:to_hash)

potrebbe essere giusto a seconda dell'applicazione.


25

Di solito in rubino quando stai cercando "tipo" in realtà stai cercando il "tipo di anatra" o "fa ciarlatano come un'anatra?". Vedresti se risponde a un certo metodo:

@some_var.respond_to?(:each)

Puoi iterare su @some_var perché risponde a: ciascuno

Se vuoi davvero conoscere il tipo e se è Hash o Array, puoi fare:

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

Questo non funzionerà se il tuo codice dipende dall'ordinamento dei dati (ad es. Se stai usando each_with_index). L'ordine degli elementi è implementata in modo diverso tra hash e array ed è differente tra le versioni rubino (. Intertwingly.net/slides/2008/oscon/ruby19/22 )
juan2raid

1
@ juan2raid: se l'ordine è importante, chiamalo sortprima.
Andrew Grimm,

Il secondo esempio di @Brandon dovrebbe convertire la classe in stringa. <br/>["Hash", "Array"].include?(@some_var.class.to_s) #=> check both through instance class
OBCENEIKON

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

questo può anche essere usato con case case

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

4

Io lo uso questo:

@var.respond_to?(:keys)

Funziona con Hash e ActiveSupport :: HashWithIndifferentAccess.


4

In pratica, spesso vorrai agire in modo diverso a seconda che una variabile sia un array o un hash, non solo una semplice spiegazione. In questa situazione, un elegante linguaggio è il seguente:

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

Si noti che non si chiama il .classmetodo item.


2

Puoi usare instance_of?

per esempio

@some_var.instance_of?(Hash)

Nota che questo non funziona per le sottoclassi ! Ad esempio, se hai un ActiveRecord::Collectione prova, instance_of?(Array)questo tornerà false, mentre is_a?(Array)tornerà true.
Tom Lord,

0

Se si desidera verificare se un oggetto è strettamente o esteso a Hash, utilizzare:

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

Ma per valutare la digitazione dell'anatra di Ruby, potresti fare qualcosa del tipo:

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

È utile quando si desidera accedere ad alcuni valori utilizzando la value[:key]sintassi.

Si noti che Array.new["key"]aumenterà a TypeError.


-1
irb(main):005:0> {}.class
=> Hash
irb(main):006:0> [].class
=> Array
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.