Ruby: пропустить элемент в цикле, если возникает исключение

У меня есть следующий метод:

def fetch_something
  @fetch_something ||= array_of_names.inject({}) do |results, name|
    begin
      results[name] = fetch_value!(name)
    rescue NoMethodError, RuntimeError
      next
    end

    results
  end
end

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

Хотя это работает нормально, я получаю сообщение об ошибке от Rubocop:

Lint/NextWithoutAccumulator: используйте next с аргументом-аккумулятором в уменьшении.

Поиск этой ошибки в Google приводит меня к http://www.rubydoc.info/gems/rubocop/0.36.0/RuboCop/Cop/Lint/NextWithoutAccumulator, где указано не пропускать аккумулятор, в результате чего метод будет выглядеть следующим образом:

def fetch_something
  @fetch_something ||= array_of_names.inject({}) do |results, name|
    begin
      results[name] = fetch_value!(name)
    rescue NoMethodError, RuntimeError
      next(name)
    end

    results
  end
end

Проблема в том, что это изменение нарушает работающий метод. Любые идеи о том, как решить эту проблему?

Update: Demonstrational example:
array_of_names = ['name1','name2','name3']

def fetch_value!(name)
  # some code that raises an error if the name doesn't correspond to anything
end

fetch_something

# => {'name1' => {key1: 'value1', ...}, 'name3' => {key3: 'value3', ...}}
# 'name2' is missing since it wasn't found durcing the lookup

person Severin    schedule 23.05.2016    source источник
comment
Как насчет того, чтобы вместо этого попробовать next(results)?   -  person user12341234    schedule 23.05.2016
comment
@user12341234 user12341234 Это тоже ломает его.   -  person Severin    schedule 23.05.2016
comment
возможно, вы могли бы показать пример ввода/вывода, чтобы оценить предполагаемое поведение?   -  person user12341234    schedule 23.05.2016
comment
@ user12341234 Добавлен пример   -  person Severin    schedule 23.05.2016
comment
Северин, см. ответ @user12341234. Я не знал, что вам не нужны ключи без значений в хэше результата.   -  person Alexey Kuznetsov    schedule 23.05.2016


Ответы (2)


Используя ваш пример кода, кажется, что next(results) действительно решает проблему для меня. Я использовал тестовый код, который выдает KeyError вместо NoMethodError или RuntimeError, но идея остается той же:

@array_of_names = ['name1','name2','name3']

def fetch_value!(name)
  {'name1' => 'n1', 'name3' => 'n3'}.fetch(name)
end

def fetch_something
  @fetch_something ||= @array_of_names.inject({}) do |results, name|
    begin
      results[name] = fetch_value!(name)
    rescue NoMethodError, RuntimeError, KeyError
      next(results)
    end

    results
  end
end

p fetch_something

Этот код выводит:

{"name1"=>"n1", "name3"=>"n3"}

Кроме того, я согласен с @Алексеем Кузнецовым в том, что each_with_object, вероятно, является подходящим вариантом, когда ваш блочный код мутирует структуру данных, которую вы пытаетесь построить. Так что моя предпочтительная реализация fetch_something может быть примерно такой:

def fetch_something
  @fetch_something ||= @array_of_names.each_with_object({}) do |name, results|
    begin
      results[name] = fetch_value!(name)
    rescue NoMethodError, RuntimeError, KeyError
      # error handling
    end
  end
end

Обратите внимание, что в моем примере блок begin/end находится вне присваивания results[name], в отличие от примера @Алексей Кузнецов, который присваивает хэшу nil каждый раз, когда возникает исключение.

person user12341234    schedule 23.05.2016
comment
Спасибо за ваш ответ! - person Severin; 24.05.2016

Просто используйте each_with_object

def fetch_something
  @fetch_something ||= array_of_names.each_with_object({}) do |name, results|
    results[name] ||= begin
                        fetch_value!(name)
                      rescue NoMethodError, RuntimeError
                      end
  end
end
person Alexey Kuznetsov    schedule 23.05.2016
comment
Это также нарушает его первоначальную функциональность. - person Severin; 23.05.2016