Какой элегантный способ сказать в Ruby, является ли переменная Hash или Array?

141

Чтобы проверить, что @some_varя делаю

if @some_var.class.to_s == 'Hash' 

Я уверен, что есть более элегантный способ проверить, @some_varявляется ли Hashили Array.

drhyde
источник
1
Эндрю: Я вызываю API, и в JSON я получаю ответ, если есть несколько результатов, я получаю массив, но если есть только один, я получаю хэш, а не один элемент массива. Есть ли лучший способ вперед, чем проверка Array vs Hash?
drhyde
2
Так вы получаете [result_hash, another_result_hash]или single_result_hash? Кто бы ни создал этот API, он не справлялся с работой!
Эндрю Гримм
2
Вы правы, Эндрю, и держу пари, что гораздо проще заставить людей, написавших API, исправить это, чем проверять хеш против массива.
Оливье Ольбаум Шерлер
У меня такая же ситуация, как у @drhyde. В моем случае третьей стороной является HTTParty, который после анализа XML-файла не может решить, как лучше всего справиться с ситуацией, когда элемент может иметь 1-n дочерних элементов.
Грязный Генри
Вам следует посмотреть скринкаст Avdi Grimms: rubytapas.com/2016/10/17/episode-451-advanced-class-membership и, возможно, заставить кабаки ответить на принятый.
Александр Пресбер

Ответы:

263

Вы можете просто сделать:

@some_var.class == Hash

или также что-то вроде:

@some_var.is_a?(Hash)

Стоит отметить, что "is_a?" Метод true, если класс находится где угодно в дереве предков объектов. например:

@some_var.is_a?(Object)  # => true

вышеупомянутое верно, если @some_var является экземпляром хеша или другого класса, который происходит от Object. Итак, если вы хотите строгое соответствие для типа класса, используя == или instance_of? Метод, вероятно, то, что вы ищете.

Пит
источник
20
is_a?это лучший вариант, так как он также возвращает trueдля подклассов.
Фабио Батиста
И, конечно, вы также не учитываете скобки, так что @som_var.is_a? Hashтоже работает :)
Гас Шортц
7
Будьте осторожны, это может действительно вас испортить, если кто-то в конечном итоге передаст вам экземпляр ActiveSupport :: HashWithIndifferentAccess. Который отвечает как хеш, но на самом деле не является хешем. :(
открывает
Мне также хотелось бы, чтобы больше подробностей касалось типизации утки, поскольку первоначальный автор явно искал рубиновый способ проверки типов.
NobodysNightmare
3
@unflores ActiveSupport::HashWithIndifferentAccessнаследуется от Hash, поэтому @some_var.is_a?(Hash)в этом случае также вернет true. (У меня был тот же вопрос и случай HashWithIndifferentAccess, и я немного запутался в вашем комментарии ... поэтому я хотел уточнить)
Stilzk1n
38

Прежде всего, лучший ответ на буквальный вопрос

Hash === @some_var

Но на этот вопрос действительно нужно было ответить, показывая, как здесь можно печатать на уток. Это немного зависит от того, какая утка вам нужна.

@some_var.respond_to?(:each_pair)

или

@some_var.respond_to?(:has_key?)

или даже

@some_var.respond_to?(:to_hash)

может быть правильным в зависимости от приложения.

кабо
источник
25

Обычно в ruby, когда вы ищете «type», вы на самом деле хотите «duck-type» или «действительно ли это кряк, как утка?». Вы увидите, отвечает ли он определенному методу:

@some_var.respond_to?(:each)

Вы можете перебрать @some_var, потому что он отвечает на: каждый

Если вы действительно хотите знать тип, и если это Hash или Array, то вы можете сделать:

["Hash", "Array"].include?(@some_var.class)  #=> check both through instance class
@some_var.kind_of?(Hash)    #=> to check each at once
@some_var.is_a?(Array)   #=> same as kind_of
Brandon
источник
Это не сработает, если ваш код зависит от порядка данных (например, если вы используете each_with_index). Порядок элементов реализуются по- разному между хэш и массивами , и она отличается от версии Ruby (. Intertwingly.net/slides/2008/oscon/ruby19/22 )
juan2raid
1
@ juan2raid: Если порядок важен, то sortсначала позвоните по нему.
Эндрю Гримм
@ Второй пример Брэндона должен преобразовать класс в строку. <br/>["Hash", "Array"].include?(@some_var.class.to_s) #=> check both through instance class
OBCENEIKON
12
Hash === @some_var #=> return Boolean

это также можно использовать с оператором case

case @some_var
when Hash
   ...
when Array
   ...
end
GutenYe
источник
4

Я использую это:

@var.respond_to?(:keys)

Это работает для Hash и ActiveSupport :: HashWithIndifferentAccess.

drinor
источник
4

На практике вам часто хочется действовать по-разному, в зависимости от того, является ли переменная массивом или хешем, а не просто сказать. В этой ситуации элегантная идиома заключается в следующем:

case item
  when Array
   #do something
  when Hash
   #do something else
end

Обратите внимание, что вы не вызываете .classметод item.

Даниэль Шмулевич
источник
2

Ты можешь использовать instance_of?

например

@some_var.instance_of?(Hash)
Шив
источник
Обратите внимание, что это не работает для подклассов ! Например, если у вас есть ActiveRecord::Collectionи попробуйте, instance_of?(Array)то это вернется false, тогда как is_a?(Array)вернется true.
Том Лорд
0

Если вы хотите проверить, является ли объект строго или расширяется Hash, используйте:

value = {}
value.is_a?(Hash) || value.is_a?(Array) #=> true

Но чтобы оценить утку Руби, вы можете сделать что-то вроде:

value = {}
value.respond_to?(:[]) #=> true

Это полезно, когда вы хотите получить доступ только к некоторому значению с использованием value[:key]синтаксиса.

Обратите внимание, что Array.new["key"]поднимет TypeError.

Виниций Бразилия
источник
-1
irb(main):005:0> {}.class
=> Hash
irb(main):006:0> [].class
=> Array
Спирос
источник