Ruby - тест для массива

265

Какой правильный способ:

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

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

или получить количество предметов в нем?

BuddyJoe
источник
7
Вы хотите фактический массив, или просто что-то вроде массива?
Кэти Ван Стоун
1
В Ruby нет безопасности типов. Не беспокойтесь о том, что ваша переменная является массивом или нет. Метод должен предполагать, что это так, и идти вперед и подсчитывать количество вызовов: my_array.count
user132447
Пожалуйста, прочитайте ответы zgchurch и DigitalRoss для более идиоматического Ruby.
ДанТ

Ответы:

516

Вы, вероятно, хотите использовать kind_of().

>> s = "something"
=> "something"
>> s.kind_of?(Array)
=> false
>> s = ["something", "else"]
=> ["something", "else"]
>> s.kind_of?(Array)
=> true
гу.
источник
31
Там же is_a?и instance_of?. См. Stackoverflow.com/questions/3893278/…
Натан Лонг
2
Проверка типов для Java. Продолжайте и просто посчитайте на переменную. Напишите модульные тесты, чтобы убедиться, что метод работает должным образом.
user132447
14
@ user132447 на самом деле java является безопасным типом, поэтому вам не нужно беспокоиться о проверке любых типов
grinch
8
Я понизил это сейчас, так как не думаю, что это хорошая практика для такого языка, как Ruby. Ответ @zgchurch - явно более идиоматический подход к вопросу. В таких случаях, мне кажется, гораздо разумнее попытаться выяснить, что означает ОП, чем слепо давать ему дробовик ...
Пер Лундберг,
1
Почему вы хотите использовать kind_of?()поверх других решений? Некоторое объяснение преимуществ вашего ответа перед другими будет полезно для будущих читателей.
AlbertEngelB
148

Вы уверены, что это должен быть массив? Вы можете использовать его respond_to?(method)так, чтобы ваш код работал для похожих вещей, которые не обязательно являются массивами (возможно, для некоторых других перечисляемых вещей). Если вам на самом деле нужен array, то Array#kind\_of?лучше всего пост, описывающий метод.

['hello'].respond_to?('each')
zgchurch
источник
1
В этом случае я уверен, что это будет массив. Но приятно знать и этот метод. +1
BuddyJoe
Интересная идея, я использую push / pop для структуры данных. Будет ли что-нибудь кроме массивов отвечать на эти методы?
Дрю
3
Если вы хотите что-то более похожее на массив, вы можете захотеть respond_to?(:to_ary).
Эндрю Гримм
21
В целом, это хорошая практика для развития ОО. Я прочитал, где кто-то сказал в принципе: не думайте, что вы вызываете методы для своих объектов. Вы отправляете им сообщения. Если объект знает, как ответить на ваше сообщение, вам все равно, к какому классу он относится, имеет ли он метод с таким именем или динамически создает ответ с помощью method_missing. Важно, может ли он ответить на ваше сообщение? Это позволяет лучше абстрагировать функцию и реализацию. Вы можете изменить, какой объект вы используете позже, если он по-прежнему правильно реагирует.
Натан Лонг
2
Единственная проблема с этим, скажем, я хочу проверить, является ли что-то итерируемое индексированным, поэтому массивы, связанные списки и т. Д. Были бы классными, но я не хочу, чтобы хранилища значений ключей типа хэшей?
Colton Voege
58

Вместо того, чтобы тестировать Array,просто преобразовать все, что вы получаете в одноуровневый, Array,ваш код должен обрабатывать только один случай.

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

Рубин имеет различные пути согласования в API , который может принимать объект или массив объектов, поэтому, принимая догадываться , почему вы хотите знать , если что - то есть массив, у меня есть предложение.

Оператор splat содержит много волшебства, которое вы можете найти, или вы можете просто вызвать, Array(something)который добавит оболочку Array, если это необходимо. Это похоже на [*something]это в одном случае.

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

Или вы можете использовать знак восклицательного знака в объявлении параметра, а затем .flatten, давая вам другой тип коллектора. (В этом отношении, Вы могли бы также позвонить .flattenвыше.)

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

И, благодаря gregschlom , иногда просто быстрее использовать, Array(x)потому что, когда он уже есть, Arrayему не нужно создавать новый объект.

DigitalRoss
источник
Итак, вы говорите, что если это один элемент, он превращает его в массив с одним элементом?
BuddyJoe
Да, и если он уже является массивом, он сохраняет его без добавления второго обертки массива.
DigitalRoss
2
Не забывайте: [*nil] => []. Таким образом, вы можете получить пустой массив.
Кристофер Оезбек
3
Использование Array(foo)гораздо эффективнее, чем[*foo]
gregschlom
23

[1,2,3].is_a? Array оценивает как истинное.

dipole_moment
источник
1
Что это добавляет к ответам, которые были на сайте в течение почти семи лет ..?
Мартин Турной
6
@Carpetsmoker нет краткого ответа, который ссылается is_a?на всю цепочку. Ближайшим является [1,2,3].is_a? Enumerable. Я все еще думаю, что стоит получить этот ответ.
dipole_moment
4
Вы знаете ... вы на самом деле правы ... Я мог бы поклясться, что видел это там раньше: - / Есть голос!
Мартин Турной
16

Похоже, вы ищете что-то, что имеет некоторое представление о предметах. Поэтому я бы порекомендовал посмотреть, если это так Enumerable. Это также гарантирует существование #count.

Например,

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

отметить , что, в то время как size, lengthи countвсе работы для массивов, countэто правильный смысл здесь - (к примеру, 'abc'.lengthи 'abc'.sizeоба работают, но 'abc'.countне работает , как это).

Осторожно: строка is_a? Enumerable, так что, возможно, это не то, что вам нужно ... зависит от вашей концепции массива, подобного объекту.

Питер
источник
11

Пытаться:

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

РЕДАКТИРОВАТЬ : другой ответ гораздо лучше, чем мой.

Лукас Джонс
источник
6

Также подумайте об использовании Array(). Из Руководства по стилю сообщества Ruby :

Используйте Array () вместо явной проверки Array или [* var], когда имеете дело с переменной, которую вы хотите рассматривать как Array, но не уверены, что это массив.

# 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
источник
Это даст неожиданные результаты при передаче хэша, потому что он to_aвызывается для каждого аргумента, добавленного в новый массив, поэтому Array({id: 100})возвращается[[:id, 100]]
brent