1

So in Python sum([]) will yield 0, which is pretty important.

Whereas in ruby [].reduce(:+) will give nil, of course the ternary operator isn't a replacement, because:

(my_complicated_mapping).empty? ? 0 : (my_complicated_mapping).reduce(:+) 

Will call my_complicated_mapping twice. Therefore the obvious method is:

res = my_complicated_mapping
res = (res.empty? ? 0 : res.reduce(:+))

I think there must be a neater way to do this though.

1
  • nil.to_i #=> 0, so you could tack to_i onto reduce, but I wouldn't recommend it. Just do as @mikej suggests. Commented May 22, 2015 at 8:35

4 Answers 4

4

With reduce you can specify the initial value as the first parameter e.g.

my_complicated_mapping.reduce(0, :+)

then if the list is empty you'll get 0 instead of nil. You can check the different alternative ways of using reduce here.

or if using a block for reduce it would be:

my_complicated_mapping.reduce(0) { |sum, n| sum + n }

i.e. a single parameter with with initial value and supplying your block.

It's important to understand why reduce returns nil in the case of the empty array: If you don't specify an explicit initial value for memo, then the first element of the array is used as the initial value of memo, which of course doesn't exist in the empty case.

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

7 Comments

What does that look like in block form? It seems kind of magical.
@user3467349 updated with another example and some more details - hope that helps make it clearer!
Ah thanks, the later example is even more useful - the reason I wasn't able to use reduce instead of map at first was because the { |sum, i| } block was assigning the first value of the range to sum - which was not a practical behaviour, specifying a value to reduce appears to fix that. Although that's even more magical! Not sure why it was designed like that (e.g. (1..4).reduce(0) {} will yield 4 iterations, where as (1..4).reduce {} yields only 3 iterations with 1 assigned to sum.
In most languages, these are two different operations, e.g. in Haskell, they are foldl :: (a -> b -> a) -> a -> [b] -> a and foldl1 :: (a -> a -> a) -> [a] -> a, in Scala, they are GenTraversableOnce[+A].foldLeft[B](z: B)(op: (B, A) ⇒ B): B and TraversableOnce[+A].reduceLeft[B >: A](op: (B, A) ⇒ B): B. In Ruby, they are overloaded into one method. But still, they are two distinct operations: in the general case, the accumulator can be of a different type than the elements and the folding operator has type (ACC, EL) -> ACC. In the restricted case, the accumulator has the same type as
the elements and the folding operator has type (EL, EL) -> EL, plus, reducing an empty list is an error.
|
2

You can use to_i to convert nil to 0

[].reduce(:+).to_i
# => 0 

Incase Array contains float values, using to_f can help:

[].reduce(:+).to_f
# => 0.0

2 Comments

What if it's an array of Float?
using .to_f would make more sense then.
1

Why not give 0 as initial parameter? Like

my_complicated_mapping.reduce(0, :+)

Comments

0

You also can use inject:

my_complicated_mapping.inject(0) { |sum, e| sum + e }

1 Comment

inject is just a synonym for reduce.

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.