Ruby - тест на масив


265

Який правильний шлях:

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

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

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


7
Ви хочете фактичний масив чи щось подібне до масиву?
Кеті Ван Стоун

1
У Рубі немає типової безпеки. Не хвилюйтеся, чи є ваша змінна масивом чи ні. Метод повинен припустити, що він є, і продовжуйте і розраховуйте дзвінки на ньому: my_array.count
user132447

Прочитайте відповіді zgchurch та DigitalRoss для більш ідіоматичного Ruby.
DanT

Відповіді:


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. Вперед і просто розраховуйте дзвінок на змінну. Напишіть одиничні тести, щоб переконатися, що метод працює так, як очікувалося.
користувач132447

14
@ user132447 насправді java є безпечним для типу, тому вам не потрібно турбуватися про перевірку будь-яких типів
посміхнутися

8
Зараз я прихилився до цього, оскільки не думаю, що це є хорошою практикою в такій мові, як Рубі. Відповідь @zgchurch - явно набагато ідіоматичніший підхід до питання. У таких випадках, я думаю, що є набагато більше сенсу спробувати зрозуміти, що означає ОП, а не сліпо давати йому рушницю ...
Пер Лундберг

1
Чому б ви хотіли використовувати kind_of?()над іншими рішеннями? Деякі пояснення щодо переваг вашої відповіді над іншими допоможуть майбутнім читачам.
AlbertEngelB

148

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

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

1
У цьому випадку я впевнений, що це буде масив. Але приємно знати і цей метод. +1
BuddyJoe

Цікава ідея: я використовую push / pop на структурі даних. Чи відповів би хто-небудь крім масивів на ці методи?
Дрю

3
Якщо ви хочете щось більш схоже на масив, ви можете захотіти respond_to?(:to_ary).
Ендрю Грімм

21
Загалом, це хороша практика розвитку ОО. Я читав, де хтось в основному сказав: не уявляю, що ти називаєш методи на своїх об'єктах. Ви надсилаєте їм повідомлення. Якщо об’єкт знає, як реагувати на ваше повідомлення, то вам не байдуже, який це клас, чи є у нього метод, який називається, або чи динамічно створюється відповідь через method_missing. Важливим є те, чи може він відповісти на ваше повідомлення? Це дозволяє краще абстрагувати функцію та реалізовувати. Ви можете змінити об’єкт, який ви використовуєте пізніше, доки він все ще відповідає правильно.
Натан Лонг

2
Єдине питання з цим полягає в тому, що я хочу перевірити, чи є щось індексованим ітерабельним, тому масиви, пов’язані списки тощо були б крутими, але я не хочу зберігати ключові значення, як хеші?
Колтон Фодж

58

Замість тестування Array,справедливої ​​конвертації того, що ви отримаєте, в однорівневий, Array,тому ваш код повинен обробляти лише один випадок.

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

У Ruby є різні способи гармонізації 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, не потрібно створювати новий об’єкт.


Отже, ви говорите, якщо це один елемент, це робить його масив з одним елементом в ньому?
BuddyJoe

Так, і якщо він вже є масивом, він зберігає його, не додаючи обгортку другого масиву.
DigitalRoss

2
Не забувайте: [*nil] => []. Тож у вас може виникнути порожній масив.
Крістофер Оезбек

3
Використання Array(foo)набагато ефективніше ніж[*foo]
gregschlom

23

[1,2,3].is_a? Array оцінює до істинного.


1
Що це додає до відповідей, які існують на сайті майже сім років ..?
Мартін Турноїй

6
@Carpetsmoker немає стислої відповіді, на яку посилаються is_a?у цілій темі. Найближчий - 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? Численні, тому, можливо, це не те, чого ви хочете ... залежить від вашої концепції об'єкта, подібного до масиву.



6

Також розглянути можливість використання Array(). З посібника зі стилю Ruby Community :

Використовуйте Array () замість явної перевірки масиву або [* var] при роботі зі змінною, яку ви хочете розглядати як масив, але ви не впевнені, що це масив.

# 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) }

Це призведе до несподіваних результатів при передачі Hash, оскільки to_aвикликається кожен аргумент, доданий до нового масиву, тому Array({id: 100})повертається[[:id, 100]]
brent
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.