Ruby - test pour tableau

265

Quelle est la bonne façon de:

is_array("something") # => false         (or 1)

is_array(["something", "else"]) # => true  (or > 1)

ou pour obtenir le nombre d'éléments en elle?

BuddyJoe
la source
7
Voulez-vous un tableau réel, ou simplement quelque chose comme un tableau?
Kathy Van Stone,
1
Il n'y a pas de sécurité de type dans Ruby. Ne vous inquiétez pas que votre variable soit un tableau ou non. La méthode doit supposer que c'est le cas, et aller de l'avant et compter les appels dessus: my_array.count
user132447
Veuillez lire les réponses de zgchurch et DigitalRoss pour un Ruby plus idiomatique.
DanT

Réponses:

516

Vous voulez probablement utiliser kind_of().

>> s = "something"
=> "something"
>> s.kind_of?(Array)
=> false
>> s = ["something", "else"]
=> ["something", "else"]
>> s.kind_of?(Array)
=> true
ry.
la source
31
Il y a aussi is_a?et instance_of?. Voir stackoverflow.com/questions/3893278/…
Nathan Long
2
La vérification de type concerne Java. Allez-y et appelez simplement count sur la variable. Écrivez des tests unitaires pour vous assurer que la méthode fonctionne comme prévu.
user132447
14
@ user132447 en fait, java est sûr pour les types, vous n'avez donc pas à vous soucier de vérifier les types
grinch
8
J'ai rétrogradé cela maintenant car je ne pense pas que ce soit une bonne pratique dans une langue comme Ruby. La réponse de @zgchurch est clairement une approche beaucoup plus idiomatique de la question. Dans des cas comme celui-ci, je pense qu'il est beaucoup plus logique d'essayer de comprendre ce que signifie l'OP, plutôt que de lui donner aveuglément un fusil de chasse ...
Per Lundberg
1
Pourquoi voudriez-vous utiliser d' kind_of?()autres solutions? Des explications sur les avantages de votre réponse par rapport aux autres pourraient être utiles aux futurs lecteurs.
AlbertEngelB
148

Êtes-vous sûr qu'il doit s'agir d'un tableau? Vous pourrez peut-être l'utiliser respond_to?(method)pour que votre code fonctionne pour des choses similaires qui ne sont pas nécessairement des tableaux (peut-être une autre chose numérotable). Si vous en avez réellement besoin array, le message décrivant la Array#kind\_of?méthode est le meilleur.

['hello'].respond_to?('each')
zgchurch
la source
1
Dans ce cas, je suis sûr que ce sera un tableau. Mais c'est bien de connaître cette méthode aussi. +1
BuddyJoe
Idée intéressante, j'utilise push / pop sur une structure de données. Est-ce que quelque chose en plus des tableaux répondrait à ces méthodes?
Drew
3
Si vous voulez quelque chose de plus semblable à un tableau, vous voudrez peut-être respond_to?(:to_ary).
Andrew Grimm
21
En général, c'est une bonne pratique pour le développement OO. J'ai lu où quelqu'un a dit essentiellement: n'imaginez pas que vous appelez des méthodes sur vos objets. Vous leur envoyez des messages. Si un objet sait comment répondre à votre message, peu importe de quelle classe il s'agit, s'il a une méthode nommée ça ou s'il crée dynamiquement une réponse via method_missing. L'important est de savoir s'il peut répondre à votre message? Cela permet une meilleure abstraction de la fonction et de la mise en œuvre. Vous pouvez changer quel objet vous utilisez plus tard, tant qu'il répond toujours correctement.
Nathan Long
2
Le seul problème avec cela est de dire que je veux vérifier si quelque chose est un itérable indexé, donc les tableaux, les listes chaînées, etc. seraient cool, mais je ne veux pas de magasins de valeurs clés comme les hachages?
Colton Voege
58

Au lieu de tester pour Array,simplement convertir tout ce que vous obtenez en un seul niveau Array,, votre code n'a besoin que de gérer le seul cas.

t = [*something]     # or...
t = Array(something) # or...
def f *x
    ...
end

Ruby a différentes façons d'harmoniser une API qui peut prendre un objet ou un tableau d'objets, donc, devinez pourquoi vous voulez savoir si quelque chose est un tableau, j'ai une suggestion.

L' opérateur splat contient beaucoup de magie que vous pouvez rechercher, ou vous pouvez simplement appeler Array(something)ce qui ajoutera un wrapper Array si nécessaire. C'est similaire à [*something]celui-ci.

def f x
  p Array(x).inspect
  p [*x].inspect
end
f 1         # => "[1]"
f [1]       # => "[1]"
f [1,2]     # => "[1, 2]"

Ou, vous pouvez utiliser le splat dans la déclaration de paramètres, puis .flattenvous donner un type de collecteur différent. (D'ailleurs, vous pouvez également appeler .flattenci - dessus.)

def f *x
  p x.flatten.inspect
end         # => nil
f 1         # => "[1]"
f 1,2       # => "[1, 2]"
f [1]       # => "[1]"
f [1,2]     # => "[1, 2]"
f [1,2],3,4 # => "[1, 2, 3, 4]"

Et, grâce à gregschlom , il est parfois plus rapide de simplement l'utiliser Array(x)parce que quand c'est déjà un, Arrayil n'a pas besoin de créer un nouvel objet.

DigitalRoss
la source
Donc, vous dites que s'il s'agit d'un seul élément, cela en fait un tableau contenant un seul élément?
BuddyJoe
Oui, et s'il s'agit déjà d'un tableau, il le conserve sans ajouter un deuxième wrapper de tableau.
DigitalRoss
2
Ne pas oublier: [*nil] => []. Vous pourriez donc vous retrouver avec un tableau vide.
Christopher Oezbek
3
L'utilisation Array(foo)est beaucoup plus efficace que[*foo]
gregschlom
23

[1,2,3].is_a? Array évalue à vrai.

moment dipolaire
la source
1
Qu'est-ce que cela ajoute aux réponses qui sont sur le site depuis près de sept ans ..?
Martin Tournoij
6
@Carpetsmoker il n'y a pas de réponse concise qui fait référence is_a?à tout ce fil. Le plus proche est a [1,2,3].is_a? Enumerable. Je pense toujours que cela vaut la peine d'avoir cette réponse.
dipole_moment
4
Vous savez .. vous avez en fait raison ... J'aurais juré avoir vu ça plus haut: - / Ayez un vote positif!
Martin Tournoij
16

Il semble que vous recherchiez quelque chose qui a un certain concept d'articles. Je recommanderais donc de voir si c'est le cas Enumerable. Cela garantit également l'existence de #count.

Par exemple,

[1,2,3].is_a? Enumerable
[1,2,3].count

notez que, tandis que size, lengthet counttous fonctionnent pour les tableaux, countest le bon sens ici - (par exemple, 'abc'.lengthet les 'abc'.sizedeux fonctionnent, mais 'abc'.countne fonctionnent pas comme ça).

Attention: une chaîne is_a? Enumerable, alors peut-être que ce n'est pas ce que vous voulez ... dépend de votre concept d'un objet de type tableau.

Peter
la source
11

Essayer:

def is_array(a)
    a.class == Array
end

EDIT : L'autre réponse est bien meilleure que la mienne.

Lucas Jones
la source
6

Pensez également à utiliser Array(). Extrait du Ruby Community Style Guide :

Utilisez Array () au lieu de la vérification explicite du tableau ou [* var], lorsque vous traitez une variable que vous souhaitez traiter comme un tableau, mais vous n'êtes pas certain qu'il s'agisse d'un tableau.

# bad
paths = [paths] unless paths.is_a? Array
paths.each { |path| do_something(path) }

# bad (always creates a new Array instance)
[*paths].each { |path| do_something(path) }

# good (and a bit more readable)
Array(paths).each { |path| do_something(path) }
GuyPaddock
la source
Cela produira des résultats inattendus lors du passage d'un Hash car to_aest appelé sur chaque argument ajouté au nouveau tableau, donc Array({id: 100})retourne[[:id, 100]]
brent