0

I have the following method:

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

To its purpose: It fetches a value for a given name that can raise an error, in which case it will ignore the name and try the next one.

While this works fine I get an error from Rubocop that says:

Lint/NextWithoutAccumulator: Use next with an accumulator argument in a reduce.

Googling that error leads me to http://www.rubydoc.info/gems/rubocop/0.36.0/RuboCop/Cop/Lint/NextWithoutAccumulator where it says to not omit the accumulator, which would result in a method looking like this:

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

The problem is, that this change breaks the otherwise working method. Any ideas on how to tackle that?

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
5
  • What about trying next(results) instead? Commented May 23, 2016 at 11:54
  • @user12341234 This breaks it as well. Commented May 23, 2016 at 12:03
  • perhaps you could show sample input/output to gauge the intended behavior? Commented May 23, 2016 at 12:52
  • @user12341234 Added example Commented May 23, 2016 at 13:07
  • Severin, see @user12341234 answer. I didn't know, that you don't need keys without values in result hash. Commented May 23, 2016 at 16:46

2 Answers 2

2

Using your example code, it seems that next(results) does fix the issue for me. I've used some test code that throws a KeyError instead of NoMethodError or RuntimeError, but the idea is still the same:

@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

This code outputs:

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

Also, I agree with @Алексей Кузнецов that each_with_object is probably the way to go whenever your block code mutates the data structure you're trying to build. So my preferred implementation of fetch_something might be more like this:

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

Note that in my example the begin/end block is outside the assignment to results[name] in contrast to @Алексей Кузнецов's example which will assign nil to the hash every time an exception occurs.

Sign up to request clarification or add additional context in comments.

Comments

0

Just use 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

1 Comment

That also breaks the original functionality of it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.