Rails 3.1: Ruby идиома, чтобы предотвратить .each от генерации исключения, если nil?

Есть ли способ использовать .each, чтобы он не выдавал ошибку, если объект нулевой или пустой (без добавления дополнительного нулевого/пустого теста?

Кажется, что если я скажу phonelist.each do |phone|, что если список телефонов пуст, то блок не должен выполняться.

Но, на мой взгляд (хамл), у меня есть - @myvar.phonelist.each do |phone|, и если список телефонов пуст, он выдает NoMethodError.

Я часто сталкиваюсь с этим и всегда обходным путем, добавляя явную проверку/ветвь для .blank? но, похоже, должен быть более простой способ сказать .each, что пустое означает ничего не делать.


person jpw    schedule 06.03.2012    source источник
comment
Вызов each для пустого перечисляемого не должен ничего делать.   -  person Andrew Marshall    schedule 07.03.2012
comment
@AndrewMarshall, но это не то, что происходит, он звонит each на nil.   -  person Gavin Miller    schedule 07.03.2012
comment
@AndrewMarshall: он имел в виду объект nil, а не пустую коллекцию.   -  person Ed S.    schedule 07.03.2012
comment
АГА. Спасибо. Глупый баг. Вспомогательный метод устанавливал значение nil not []. Спасибо за ваши комментарии.   -  person jpw    schedule 07.03.2012
comment
В вопросе говорится, что он не выдает ошибку, если объект равен нулю или пуст, я комментировал этот факт.   -  person Andrew Marshall    schedule 07.03.2012
comment
@jpwynn Редактировать свой вопрос с помощью SOLVED и объяснения - не очень хорошая идея. Либо примите один из ответов, если они решили его, либо опубликуйте свой собственный ответ с решением.   -  person Andrew Marshall    schedule 07.03.2012


Ответы (6)


Не могу поверить, что никто еще не предложил это:

(@myvar.phonelist || []).each do |phone|
   ...

Если phonelist равно nil, each зациклится на пустом массиве, выполняя блок ноль раз.

ОДНАКО это все равно вызовет исключение, если phonelist не является перечисляемым (например, массивом).

person Grant Birchmeier    schedule 11.11.2019

Вы можете использовать метод try для вызова .each для нулевого значения. поэтому он не выдает ошибку, если объект равен нулю или пуст.

phonelist = nil
phonelist.try(:each){|i| puts i}
person johnnyx25    schedule 07.03.2012
comment
Думаю, я не понимаю, почему люди прибегают к чему-то подобному, когда было бы оптимально просто гарантировать, что то, что никогда не должно быть нулевым, никогда не будет нулевым. Если это невозможно сделать (по какой-либо причине), зачем вам делать это вместо простого оператора if? Я не понимаю. - person Ed S.; 08.03.2012
comment
Я согласен с тем, что было бы оптимально гарантировать, что вы никогда не получите nil, но я не понимаю, почему добавление оператора if является лучшим решением, чем использование try. Любой подход должен работать. - person johnnyx25; 18.03.2012
comment
Я не думаю, что повторение имен переменных понятнее или проще в каждой ситуации (особенно если используются длинные имена переменных), но в try хорошо то, что вы можете продолжать связывать методы, не разбивая их. с если. Это действительно зависит от ситуации, конечно. - person johnnyx25; 19.03.2012
comment
Это умно и чисто, но все же просто пластырь. Я согласен с Эдом С., что вы должны сделать что-то другое с объектом, чтобы он возвращал что-то отличное от nil (например, пустой массив). - person Matthew Clark; 17.01.2014

Просто сделайте следующее:

Array(phonelist).each do |phone|
  #deal with your phone
end

Array(my_variable) гарантирует возврат массива, если my_variable равно нулю.

Он не создает новый массив, если my_variable уже является массивом, поэтому его безопасно и легко использовать везде, где вы хотите!

person Arkan    schedule 19.09.2013
comment
Мне это нравится. Просто и элегантно - person idrinkpabst; 25.07.2014

Вы пытаетесь прилепить пластырь к более серьезной проблеме.

В Ruby есть концепция нуля; не могу обойти это. Если вы вызываете метод для nil, вы предполагаете, что он допустим, т. е. ваш дизайн предполагает, что он допустим. Итак, вопрос на самом деле заключается в следующем: где дыра в вашем дизайне? Почему ваше предположение неверно?

Проблема здесь не в том, что вы не можете вызывать произвольные методы для объекта, который их не поддерживает; проблема в том, что ваши данные считаются достоверными, хотя очевидно, что это не всегда так.

Но, на мой взгляд (хамл), у меня есть - @myvar.phonelist.each do |phone| а если список телефонов пуст, выдается NoMethodError.

Нет. Если phonelist не является объектом, реализующим .each, возникает ошибка. Очень разные.

Вы всегда можете инициализировать его пустым массивом, если он равен нулю, т. е. phonelist ||= [], но я бы предпочел дизайн, который по возможности обеспечивает достоверные данные.

person Ed S.    schedule 06.03.2012
comment
Иногда вы не контролируете структуры данных, возвращаемые API. Вещи будут нулевыми, вопрос в силе - person Yuval Rimar; 24.04.2019
comment
Хорошо, тогда, очевидно, вам нужно проверить значение null в этой ситуации. Не понимаю, какое это имеет отношение к этому вопросу. - person Ed S.; 29.05.2019

Если вы получаете phonelist из хэша (например, проанализированного файла JSON), вы можете использовать fetch с [] по умолчанию.

phonelist = my_data.fetch('phonelist', [])
person proski    schedule 05.01.2016

Просто убедитесь, что пустое phonelist является значением [], а не nil.

В качестве альтернативы значение nil является ложным в Ruby, поэтому вы можете использовать ноль-каламбур

if phonelist
   phonelist.each do |phone|
     ...
person user7610    schedule 16.03.2018