Який елегантний спосіб у Ruby дізнатись, чи є змінною Hash або Array?


140

Щоб перевірити, що @some_varтаке, я роблю

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

Я впевнений, що є більш елегантний спосіб перевірити, чи @some_varє це Hashчи є Array.


1
Ендрю: Я дзвоню API і в JSON я повертаюсь, якщо є кілька результатів, я отримую масив, але якщо є лише один, я отримую хеш, а не один елемент масиву. Чи є кращий форвард, ніж перевірка Array vs Hash?
дрид

2
Таким чином , ви отримуєте або [result_hash, another_result_hash]або single_result_hash? Хто б не створив цей API, не робив гарної роботи!
Ендрю Грімм

2
Ви маєте рацію, Ендрю, і я думаю, що набагато простіше змусити людей, які написали API, виправити його, ніж перевіряти хеш на масив.
Олів'є 'Ельбаум' Шерлер

У мене така ж ситуація, як @drhyde. У моєму випадку третьою стороною є HTTParty, який після розбору файлу XML не може вирішити, який найкращий спосіб вирішити ситуацію, коли елемент може мати 1-н дітей.
Брудний Генрі

Ви повинні дивитись екранізацію Авді Гріммса: rubytapas.com/2016/10/17/episode-451-advanced-class-membership і, ймовірно, змусити кабо відповісти на прийняте.
Олександр Пресбер

Відповіді:


261

Ви можете просто зробити:

@some_var.class == Hash

або також щось на кшталт:

@some_var.is_a?(Hash)

Варто зазначити, що "is_a?" метод вірний, якщо клас знаходиться де-небудь в дереві родовідних об'єктів. наприклад:

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

вищевикладене справедливо, якщо @some_var є екземпляром хеш-класу чи іншого класу, що походить від Object. Отже, якщо ви хочете сувору відповідність типу класу, використовуючи == або instance_of? метод, мабуть, те, що ви шукаєте.


20
is_a?є найкращим варіантом, оскільки він також повертається trueдля підкласів.
Фабіо Батіста

І, звичайно, ви також залишаєте дужки, тому @som_var.is_a? Hashтеж працює :)
Gus Shortz

7
Будьте уважні, це може насправді зіпсувати вас, якщо хтось у кінцевому підсумку передасть вам екземпляр ActiveSupport :: HashWithIndifferentAccess. Що відповідає як хеш, але насправді не є хеш. :(
розблоковується

Мені також сподобалося б більше відповідей, які детально розглядаються щодо набору качок, оскільки оригінальний автор, очевидно, шукав рубіновий спосіб перевірки типу.
NobodysNightmare

3
@unflores ActiveSupport::HashWithIndifferentAccessуспадковує від Hash, отже @some_var.is_a?(Hash), повернеться і в цьому випадку. (У мене було саме те саме питання, що стосується і випадку 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

Зазвичай в рубіні, коли ви шукаєте "тип", ви насправді хочете "типу качки" або "чи це брязкає, як качка?". Ви побачите, чи відповідає він певному методу:

@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

Це не спрацює, якщо ваш код залежить від впорядкування даних (наприклад, якщо ви використовуєте every_with_index). Порядок елементів реалізуються по- різному між хеш і масивами , і вона відрізняється від версії Ruby (. Intertwingly.net/slides/2008/oscon/ruby19/22 )
juan2raid

1
@ juan2raid: Якщо замовлення важливе, sortспочатку зателефонуйте .
Ендрю Грімм

@Brandon другий приклад повинен перетворити клас у рядок. <br/>["Hash", "Array"].include?(@some_var.class.to_s) #=> check both through instance class
OBCENEIKON

12
Hash === @some_var #=> return Boolean

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

case @some_var
when Hash
   ...
when Array
   ...
end

4

Я використовую це:

@var.respond_to?(:keys)

Він працює для Hash і ActiveSupport :: HashWithIndifferentAccess.


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

Якщо ви хочете перевірити, чи об'єкт є строго або розширює a Hash, використовуйте:

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

Але, щоб мати значення набору качок Рубі, ви можете зробити щось на кшталт:

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

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

Зверніть увагу, що Array.new["key"]буде піднято a TypeError.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.