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.