Ruby: prueba de matriz

265

¿Cuál es la forma correcta de:

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

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

o para obtener el recuento de elementos en él?

BuddyJoe
fuente
77
¿Quieres una matriz real, o simplemente algo similar a una matriz?
Kathy Van Stone el
1
No hay seguridad de tipografía en Ruby. No se preocupe si su variable es una matriz o no. El método debe suponer que es así y seguir adelante y llamar a contar con él:
my_array.count
Por favor, lea las respuestas de zgchurch y DigitalRoss para un Ruby más idiomático.
DanT

Respuestas:

516

Probablemente quieras usar kind_of().

>> s = "something"
=> "something"
>> s.kind_of?(Array)
=> false
>> s = ["something", "else"]
=> ["something", "else"]
>> s.kind_of?(Array)
=> true
ry.
fuente
31
También hay is_a?y instance_of?. Ver stackoverflow.com/questions/3893278/…
Nathan Long
2
La verificación de tipos es para Java. Siga adelante y solo llame al conteo en la variable. Escriba pruebas unitarias para asegurarse de que el método funcione como se esperaba.
user132447
14
@ user132447 en realidad, java es de tipo seguro, por lo que no debe preocuparse por verificar ningún tipo
grinch
8
Lo rechacé ahora porque no creo que sea una buena práctica en un lenguaje como Ruby. La respuesta de @zgchurch es claramente un enfoque mucho más idiomático a la pregunta. En casos como este, creo que tiene mucho más sentido intentar averiguar qué significa el OP, en lugar de darle una escopeta a ciegas ...
Per Lundberg
1
¿Por qué querrías usar kind_of?()sobre otras soluciones? Alguna explicación sobre los beneficios de su respuesta sobre otros sería útil para futuros lectores.
AlbertEngelB
148

¿Estás seguro de que debe ser una matriz? Es posible que pueda usarlo respond_to?(method)para que su código funcione para cosas similares que no son necesariamente matrices (tal vez alguna otra cosa numerable). Si realmente necesita un array, entonces la publicación que describe el Array#kind\_of?método es la mejor.

['hello'].respond_to?('each')
zgchurch
fuente
1
En este caso, estoy seguro de que será una matriz. Pero es bueno saber este método también. +1
BuddyJoe el
Idea interesante, estoy usando push / pop en una estructura de datos. ¿Algo más que las matrices respondería a esos métodos?
Dibujó
3
Si desea algo más parecido a una matriz, es posible que desee respond_to?(:to_ary).
Andrew Grimm
21
En general, esta es una buena práctica para el desarrollo de OO. Leí donde alguien dijo básicamente: no te imagines que estás llamando métodos a tus objetos. Les estás enviando mensajes. Si un objeto sabe cómo responder a su mensaje, no le importa de qué clase es, o si tiene un método llamado eso, o si está creando dinámicamente una respuesta a través de method_missing. Lo importante es, ¿puede responder a su mensaje? Esto permite una mejor abstracción de la función y la implementación. Puede cambiar el objeto que usa más adelante, siempre que siga respondiendo correctamente.
Nathan Long
2
El único problema con esto es decir que quiero verificar si algo es un iterable indexado, por lo que las matrices, las listas vinculadas, etc. serían geniales, pero ¿no quiero almacenes de valores clave como hashes?
Colton Voege
58

En lugar de probar un Array,solo convertir lo que obtienes en un nivel, Array,por lo que tu código solo necesita manejar un caso.

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

Ruby tiene varias formas de armonizar una API que puede tomar un objeto o una matriz de objetos, por lo tanto, adivina por qué quieres saber si algo es una matriz, tengo una sugerencia.

El operador splat contiene mucha magia que puede buscar, o simplemente puede llamar Array(something)para agregar un contenedor de matriz si es necesario. Es similar a [*something]en este caso.

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

O bien, puede usar el símbolo splat en la declaración de parámetros y .flatten, luego , darle un tipo diferente de recopilador. (Para el caso, también puede llamar .flattenarriba).

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

Y, gracias gregschlom , a veces es más rápido simplemente usarlo Array(x)porque cuando ya es un Arrayno necesita crear un nuevo objeto.

DigitalRoss
fuente
Entonces, ¿está diciendo que si se trata de un solo elemento, lo convierte en una matriz con un solo elemento?
BuddyJoe
Sí, y si ya es una matriz, la mantiene sin agregar un segundo contenedor de matriz.
DigitalRoss
2
No se olvide: [*nil] => []. Entonces podrías terminar con una matriz vacía.
Christopher Oezbek
3
Usar Array(foo)es mucho más eficiente que[*foo]
gregschlom
23

[1,2,3].is_a? Array se evalúa como verdadero

momento bipolar
fuente
1
¿Qué agrega esto a las respuestas que han estado en el sitio durante casi siete años ...?
Martin Tournoij
66
@Carpetsmoker no hay una respuesta concisa que haga referencia is_a?en todo este hilo. Lo más cercano es a [1,2,3].is_a? Enumerable. Todavía creo que vale la pena tener esta respuesta.
dipole_moment
44
Sabes ... en realidad tienes razón ... Podría haber jurado que vi eso allí antes: - / ¡Haz un voto!
Martin Tournoij
16

Parece que buscas algo que tiene algún concepto de elementos. Por lo tanto, recomiendo ver si es así Enumerable. Eso también garantiza la existencia de#count .

Por ejemplo,

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

en cuenta que, si bien size, lengthy counttodo el trabajo para las matrices, countes el significado aquí - (por ejemplo, 'abc'.lengthy 'abc'.sizetanto trabajo, pero 'abc'.countno funciona de esa manera).

Precaución: una cadena es_a? Enumerable, entonces quizás esto no sea lo que quieres ... depende de tu concepto de una matriz como objeto.

Peter
fuente
11

Tratar:

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

EDITAR : La otra respuesta es mucho mejor que la mía.

Lucas Jones
fuente
6

También considere usar Array(). De la Guía de estilo de comunidad de Ruby :

Use Array () en lugar de la verificación explícita de Array o [* var], cuando trate con una variable que desea tratar como Array, pero no está seguro de que sea una matriz.

# 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
fuente
Esto producirá resultados inesperados al pasar un hash porque to_ase invoca en cada argumento agregado a la nueva matriz, por lo que Array({id: 100})regresa[[:id, 100]]
brent