Ruby - test na tablicę

265

Jaki jest właściwy sposób:

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

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

lub uzyskać liczbę elementów w nim?

BuddyJoe
źródło
7
Czy chcesz rzeczywistą tablicę, czy po prostu coś podobnego do tablicy?
Kathy Van Stone,
1
W Ruby nie ma bezpieczeństwa typu. Nie martw się, że twoja zmienna jest tablicą czy nie. Metoda powinna zakładać, że tak jest, i śmiało, a na nią liczyć: my_array.count
user132447
Proszę przeczytać odpowiedzi zgchurch i DigitalRoss, aby uzyskać więcej idiomatycznych Ruby.
DanT

Odpowiedzi:

516

Prawdopodobnie chcesz użyć kind_of().

>> s = "something"
=> "something"
>> s.kind_of?(Array)
=> false
>> s = ["something", "else"]
=> ["something", "else"]
>> s.kind_of?(Array)
=> true
ry.
źródło
31
Jest też is_a?i instance_of?. Zobacz stackoverflow.com/questions/3893278/...
Nathan Long
2
Sprawdzanie typu dotyczy Java. Śmiało i po prostu policz licznik na zmiennej. Napisz testy jednostkowe, aby upewnić się, że metoda działa zgodnie z oczekiwaniami.
user132447,
14
@ user132447 faktycznie java jest bezpieczna dla typów, więc nie musisz się martwić sprawdzaniem jakichkolwiek typów
grinch
8
Głosowałem za tym teraz, ponieważ nie sądzę, że jest to dobra praktyka w języku takim jak Ruby. Odpowiedź @zgchurch jest zdecydowanie bardziej idiomatycznym podejściem do pytania. W takich przypadkach myślę, że sensowniej jest spróbować dowiedzieć się, co oznacza OP, niż ślepo oddać mu strzelbę ...
Per Lundberg
1
Dlaczego chcesz korzystać kind_of?()z innych rozwiązań? Niektóre wyjaśnienia na temat zalet twojej odpowiedzi w porównaniu z innymi byłyby pomocne dla przyszłych czytelników.
AlbertEngelB
148

Czy na pewno musi to być tablica? Możesz być w stanie użyć, respond_to?(method)aby twój kod działał dla podobnych rzeczy, które niekoniecznie są tablicami (być może jakieś inne rzeczy, które można policzyć). Jeśli naprawdę potrzebujesz array, to post opisujący Array#kind\_of?metodę jest najlepszy.

['hello'].respond_to?('each')
zgchurch
źródło
1
W tym przypadku jestem pewien, że będzie to tablica. Ale miło też poznać tę metodę. +1
BuddyJoe,
Ciekawy pomysł, używam push / pop na strukturze danych. Czy cokolwiek oprócz tablic zareagowałoby na te metody?
Drew
3
Jeśli chcesz czegoś bardziej podobnego do tablicy, możesz chcieć respond_to?(:to_ary).
Andrew Grimm,
21
Zasadniczo jest to dobra praktyka w zakresie rozwoju OO. Czytam tam, gdzie ktoś powiedział w zasadzie: nie wyobrażaj sobie, że nazywasz metody na swoich obiektach. Wysyłasz im wiadomości. Jeśli obiekt wie, jak odpowiedzieć na twoją wiadomość, nie obchodzi cię, jaka to klasa, ani czy ma tak zwaną metodę, czy też dynamicznie tworzy odpowiedź poprzez metodę brak. Ważne jest to, czy może odpowiedzieć na twoją wiadomość? Pozwala to na lepszą abstrakcję funkcji i implementacji. Możesz zmienić obiekt, którego użyjesz później, o ile nadal będzie poprawnie reagował.
Nathan Long
2
Jedyny problem polega na tym, że chcę sprawdzić, czy coś jest indeksowalne, więc tablice, listy połączone itp. Byłyby fajne, ale nie chcę magazynów wartości kluczowych, takich jak skróty?
Colton Voege,
58

Zamiast testować Array,tylko konwersję tego, co dostajesz, na jeden poziom, Array,więc Twój kod musi obsłużyć tylko jeden przypadek.

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

Ruby ma różne sposoby zharmonizowania interfejsu API, który może przyjmować obiekt lub tablicę obiektów, więc zgadując, dlaczego chcesz wiedzieć, czy coś jest tablicą, mam sugestię.

Ikona operatora zawiera wiele magii można sprawdzić, czy można po prostu zadzwonić Array(something), która doda opakowanie Array razie potrzeby. Podobnie jest [*something]w tym jednym przypadku.

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

Lub możesz użyć ikony w deklaracji parametru, a następnie .flatten, dając ci inny rodzaj kolektora. (W tym przypadku możesz też zadzwonić .flattenpowyżej).

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

Dzięki, dzięki gregschlom , czasem jest to szybsze w użyciu, Array(x)ponieważ gdy już jest Array, nie musi tworzyć nowego obiektu.

DigitalRoss
źródło
Mówisz więc, że jeśli jest to pojedynczy element, to tworzy tablicę z jednym elementem?
BuddyJoe,
Tak, a jeśli jest już tablicą, zachowuje ją bez dodawania drugiego opakowania tablicy.
DigitalRoss,
2
Nie zapomnij: [*nil] => []. Więc możesz skończyć z pustą tablicą.
Christopher Oezbek
3
Korzystanie Array(foo)jest znacznie wydajniejsze niż[*foo]
gregschlom,
23

[1,2,3].is_a? Array ocenia na prawdę.

moment dipolowy
źródło
1
Co to dodaje do odpowiedzi, które są dostępne na stronie od prawie siedmiu lat ..?
Martin Tournoij,
6
@Carpetsmoker nie ma zwięzłej odpowiedzi, która odwołuje się is_a?do tego całego wątku. Najbliższy to [1,2,3].is_a? Enumerable. Nadal uważam, że warto mieć tę odpowiedź.
dipole_moment
4
Wiesz ... masz rację ... Mógłbym przysiąc, że widziałem to wcześniej: - / Mam głos!
Martin Tournoij,
16

Wygląda na to, że szukasz czegoś, co ma jakieś pojęcie o przedmiotach. Polecam więc sprawdzić, czy tak jest Enumerable. To gwarantuje również istnienie #count.

Na przykład,

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

pamiętać, że o ile size, lengthi countcała praca na macierzach, countto właściwy sens tutaj - (na przykład, 'abc'.lengthi 'abc'.sizezarówno do pracy, ale 'abc'.countnie działa w ten sposób).

Uwaga: ciąg jest_a? Wymienne, więc być może nie tego chcesz ... zależy od twojej koncepcji obiektu typu tablica.

Piotr
źródło
11

Próbować:

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

EDYCJA : Inna odpowiedź jest znacznie lepsza niż moja.

Lucas Jones
źródło
6

Rozważ również użycie Array(). Z przewodnika po stylu społeczności Ruby :

Używaj Array () zamiast jawnego sprawdzania Array lub [* var], gdy masz do czynienia ze zmienną, którą chcesz traktować jako Array, ale nie masz pewności, że jest to tablica.

# 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
źródło
Spowoduje to nieoczekiwane wyniki przy przekazywaniu skrótu, ponieważ to_ajest wywoływany dla każdego argumentu dodanego do nowej tablicy, więc Array({id: 100})wraca[[:id, 100]]
brent