I'm new to Ruby, and I'm having a strange problem with the inject method.
When I do:
(1..10).inject(0) {|count,x| count + 1}
the result is 10, as expected. But when I do
(1..10).inject(0) {|count,x| count + 1 if (x%2 == 0)}
I get an error:
NoMethodError: undefined method `+' for nil:NilClass
from (irb):43
from (irb):43:in `inject'
from (irb):43:in `each'
from (irb):43:in `inject'
from (irb):43
I don't really understand why (presumably) count is nil in the second example, but not the first. In any case, how would I count evens from 1 to 10 using inject?
The expression count + 1 if (x%2 == 0)
returns nil
when the condition isn't true, which count
gets set to because that's the nature of the inject method.
You could fix it by returning count + 1
when it's an even number and just count
when it's not:
(1..10).inject(0) { |count,x| x % 2 == 0 ? count + 1 : count }
A completely different solution is to use select
to select the even numbers and use the Array#length
method to count them.
(1..10).select { |x| x % 2 == 0 }.length
As yjerem
already pointed out, count + 1 if (x%2 == 0)
will be evaluated to nil
when x is odd. And , here is problem: the nil
value will be assigned to count
, so the next iteration will be nil + 1
,which caused error reported.
It is important to understand how inject works(a copy from ruby-doc)
enum.inject(initial) {| memo, obj | block } => obj
enum.inject {| memo, obj | block } => obj
Combines the elements of enum by applying the block to an accumulator value (memo) and each element in turn. At each step, memo is set to the value returned by the block. The first form lets you supply an initial value for memo. The second form uses the first element of the collection as a the initial value (and skips that element while iterating).
A rule will keep you away from this kind of error : the block should always return the same type of value as that of the accumulator. If your example, the block will return a type of nil
when x%2==0
if false
.
(1..10).inject(0) {|count,x| count + 1 if (x%2 == 0)}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With