Ruby - teste para array


265

Qual é o caminho certo para:

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

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

ou para obter a contagem de itens?


7
Você quer uma matriz real ou apenas algo parecido com uma matriz?
Kathy Van Stone

1
Não há segurança de tipo no Ruby. Não se preocupe se sua variável é uma matriz ou não. O método deve assumir que é e, em
seguida,

Leia as respostas de zgchurch e DigitalRoss para obter mais Ruby idiomático.
DanT 25/03

Respostas:


516

Você provavelmente quer usar kind_of().

>> s = "something"
=> "something"
>> s.kind_of?(Array)
=> false
>> s = ["something", "else"]
=> ["something", "else"]
>> s.kind_of?(Array)
=> true

31
Há também is_a?e instance_of?. Veja stackoverflow.com/questions/3893278/…
Nathan Long

2
A verificação de tipo é para Java. Vá em frente e apenas conte com a variável. Escreva testes de unidade para garantir que o método funcione conforme o esperado.
precisa saber é o seguinte

14
@ user132447 realmente java é o tipo de seguro, assim você não precisa se preocupar em verificar quaisquer tipos
grinch

8
Eu diminuí a votação agora, pois não acho que essa seja uma boa prática em um idioma como Ruby. A resposta de @zgchurch é claramente uma abordagem muito mais idiomática da questão. Em casos como este, eu acho que faz muito mais sentido para tentar descobrir o que os meios OP, ao invés de dar cegamente lhe uma espingarda ...
Per Lundberg

1
Por que você gostaria de usar kind_of?()outras soluções? Alguma explicação sobre os benefícios de sua resposta sobre os outros seria útil para futuros leitores.
AlbertEngelB

148

Tem certeza de que precisa ser uma matriz? Você pode usá- respond_to?(method)lo para que seu código funcione para coisas semelhantes que não são necessariamente matrizes (talvez alguma outra coisa enumerável). Se você realmente precisa de um array, a publicação que descreve o Array#kind\_of?método é a melhor.

['hello'].respond_to?('each')

1
Nesse caso, tenho certeza de que será uma matriz. Mas é bom conhecer esse método também. 1
BuddyJoe 7/09/09

Idéia interessante, estou usando push / pop em uma estrutura de dados. Alguma coisa além de matrizes responderia a esses métodos?
Drew

3
Se você quiser algo mais parecido com um array, você pode querer respond_to?(:to_ary).
Andrew Grimm

21
Em geral, essa é uma boa prática para o desenvolvimento de OO. Li onde alguém disse basicamente: não imagine que você está chamando métodos em seus objetos. Você está enviando mensagens para eles. Se um objeto sabe como responder à sua mensagem, você não se importa com a classe, ou se possui um método chamado esse ou se está criando dinamicamente uma resposta via method_missing. O importante é que ele pode responder à sua mensagem? Isso permite uma melhor abstração de função e implementação. Você pode alterar qual objeto você usa posteriormente, desde que ele ainda responda corretamente.
Nathan Long

2
O único problema com isso é dizer que quero verificar se algo é iterável indexado, para que matrizes, listas vinculadas etc. sejam legais, mas não quero armazenamentos de valores-chave como hashes?
Colton Voege

58

Em vez de testar uma Array,conversão, basta converter o que você tiver em um nível, Array,para que seu código precise lidar apenas com um caso.

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

O Ruby tem várias maneiras de harmonizar uma API que pode pegar um objeto ou uma matriz de objetos, portanto, adivinhando por que você quer saber se algo é uma matriz, tenho uma sugestão.

O operador splat contém muita mágica que você pode procurar ou pode simplesmente chamar, Array(something)que adicionará um wrapper Array, se necessário. É semelhante [*something]neste caso.

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

Ou você pode usar o splat na declaração de parâmetro e .flatten, em seguida , fornecer um tipo diferente de coletor. (Nesse caso, você também pode ligar .flattenacima.)

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

E, graças ao gregschlom , às vezes é mais rápido usar apenas Array(x)porque, quando já é um Array, não é necessário criar um novo objeto.


Então você está dizendo que, se for um único item, ele será uma matriz com um único item?
22610 BuddyJoe

Sim, e se já é uma matriz, ela a mantém sem adicionar um segundo wrapper de matriz.
DigitalRoss

2
Não se esqueça: [*nil] => []. Então você pode acabar com uma matriz vazia.
Christopher Oezbek 4/04

3
O uso Array(foo)é muito mais eficiente do que[*foo]
gregschlom 19/07/2016

23

[1,2,3].is_a? Array avalia como verdadeiro.


1
O que isso acrescenta às respostas que estão no site há quase sete anos ..?
Martin Tournoij 16/03

6
@Carpetsmoker, não há uma resposta concisa que faça referência a is_a?todo esse tópico. O mais próximo é um [1,2,3].is_a? Enumerable. Ainda acho que vale a pena ter essa resposta.
21416 Dipple_moment

4
Você sabe .. você está realmente certo ... Eu poderia jurar que vi isso lá em cima mais cedo: - / Faça uma votação!
Martin Tournoij 16/03 '15

16

Parece que você está procurando algo que tenha algum conceito de itens. Eu, portanto, recomendo ver se é Enumerable. Isso também garante a existência de#count .

Por exemplo,

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

note que, enquanto size , lengthe counttodo o trabalho para arrays, counté o significado aqui - (por exemplo, 'abc'.lengthe 'abc'.sizetanto trabalho, mas 'abc'.countnão é assim que funciona).

Cuidado: uma string é_a? Enumerável, então talvez isso não seja o que você deseja ... depende do seu conceito de uma matriz como objeto.


11

Experimentar:

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

EDIT : A outra resposta é muito melhor que a minha.


6

Considere também usar Array(). No Guia de estilo da comunidade Ruby :

Use Array () em vez de verificação explícita de Array ou [* var], ao lidar com uma variável que você deseja tratar como uma matriz, mas não tem certeza de que é uma 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) }

Isso produzirá resultados inesperados ao passar um Hash, porque to_aé chamado em cada argumento adicionado à nova matriz, então Array({id: 100})retorna[[:id, 100]]
brent
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.