How to use the following with an injection in ruby

I wrote code like:

[1,2,3,4,5].inject([]) do |res, a| res << a*a and next if a == 2 res << a end 

It gives the following error:

 NoMethodError: undefined method `<<' for nil:NilClass 

As in the following case, it makes the res variable as nil , how to get around this problem?

I tried different ways, but could not get close to working with ruby, I know that this fragment that I provided can be executed without the following ( a == 4 ? res << a*a : res << a ) but in my actual use case I have some complicated logic and I can. This is done simply.

+7
ruby ruby-on-rails
source share
2 answers

Replace

 res << a*a and next if a == 2 

from

 next res << a*a if a == 2 

Now it will work.

Example: -

 #!/usr/bin/env ruby ar = [1,2,3,4,5].inject([]) do |res, a| next res << a*a if a == 2 res << a end p ar # >> [1, 4, 3, 4, 5] 

Read the documentation next

next can take a value that will be the value returned for the current iteration of the block ....

+9
source share

Ugh. Do not use conventions for this. Instead, this is easy to do with the standard if / else :

 [1,2,3,4,5].inject([]) do |res, a| if a == 2 res << a*a else res << a end end # => [1, 4, 3, 4, 5] 

At any time, when you feel that you are encoding yourself into a corner, do not look for a way out, instead, back up and see what you are trying to do to see if there is an easier way there.

I would probably modify the code a bit, but for readability. Long-term maintenance depends on a quick understanding of what is happening, and code that is minimized or not obvious may later bear it:

 [1,2,3,4,5].inject([]) do |res, a| if a == 2 res << a*a else res << a end res # return it for clarity in what the block is returning end # => [1, 4, 3, 4, 5] 

inject is similar to each_with_object , only it relies on returning the battery at the end of the block, so I would like to add res at the end of the block for clarity because of if . Switching to each_with_object eliminates this dependency on the return value of the block, allowing you to make the following code more logical:

 [1,2,3,4,5].each_with_object([]) do |a, ary| if a == 2 ary << a*a else ary << a end end # => [1, 4, 3, 4, 5] 

Of course, at this point, all this can be further reduced and can use the triple version using:

 [1,2,3,4,5].each_with_object([]) do |a, ary| a2 = (a == 2) ? a * a : a ary << a2 end # => [1, 4, 3, 4, 5] 

Which of the above two more readable ones is somewhat dependent on the person who encodes it and the person responsible for maintaining it. I am inclined to the non-ternary version, because it is easier to expand / expand and does not have the linear noise of a triple circuit ?: .


Since the task was set in the comments, map reduces some noise and how we should transform the array:

 [1,2,3,4,5].map { |a| (a == 2) ? a * a : a } 

This is not verified, but it looks correct.

+2
source share

All Articles