I have an array of integers.
For example:
array = [123,321,12389]
Is there any nice way to get the sum of them?
I know, that
sum = 0
array.each { |a| sum+=a }
would work.
For ruby >= 2.4 you can use sum:
array.sum
For ruby < 2.4 you can use inject:
array.inject(0, :+)
Note: the 0 base case is needed otherwise nil will be returned on empty arrays:
> [].inject(:+)
nil
> [].inject(0, :+)
0
array.map(&:price).inject(0, :+) is a bit safer. It makes sure that if you have an empty list you get 0 instead of nil.array.inject(0) { |sum, product| sum += product.price }Try this:
array.inject(0){ |sum, x| sum + x }
See Ruby's Enumerable Documentation
(note: the 0 base case is needed so that 0 will be returned on an empty array instead of nil)
array.inject(:+) is more efficient.array.inject(:+) seems to cause trouble in Ruby 1.8.6 Exceptions " LocalJumpError : no block given" might pop up.array.sum might give you sum of the array values.reduce, which is an alias of inject (as in array.reduce( :+ )).inject rather than reduce.array.reduce(0, :+)
While equivalent to array.inject(0, :+), the term reduce is entering a more common vernacular with the rise of MapReduce programming models.
inject, reduce, fold, accumulate, and compress are all synonymous as a class of folding functions. I find consistency across your code base most important, but since various communities tend to prefer one word over another, it’s nonetheless useful to know the alternatives.
To emphasize the map-reduce verbiage, here’s a version that is a little bit more forgiving on what ends up in that array.
array.map(&:to_i).reduce(0, :+)
Some additional relevant reading:
reduce tells me more of what the function does, but inject does sound much cooler.reduce and map as higher-order functions predate MapReduce. The inspiration runs the other way. And in the MapReduce sense, it's a somewhat different operation than a simple functional reduce, having implications for how different machines communicate.Alternatively (just for comparison), if you have Rails installed (actually just ActiveSupport):
require 'activesupport'
array.sum
activesupport is a massive dependency to drag into a project to go from array.inject(:+) to array.sum.require 'active_support/core_ext/enumerable' without the .rb suffix, since that's added implicitly.For Ruby >=2.4.0 you can use sum from Enumerables.
[1, 2, 3, 4].sum
It is dangerous to mokeypatch base classes. If you like danger and using an older version of Ruby, you could add #sum to the Array class:
class Array
def sum
inject(0) { |sum, x| sum + x }
end
end
Array#sum; namely, they take an optional argument and an optional block. Defining Array#sum as you have would result in an exception being raised wherever sum appeared in the code with an argument or block.Array is empty, like if you want to return a Money object instead of an Integer, you can do something like array.sum( 0.to_money( "USD" ) ).You can use the aptly named method Enumerable#sum. It has a lot of advantages over inject(:+) but there are some important notes to read at the end as well.
(1..100).sum
#=> 5050
[1, 2, 4, 9, 2, 3].sum
#=> 21
[1.9, 6.3, 20.3, 49.2].sum
#=> 77.7
This method is not equivalent to #inject(:+). For example
%w(a b c).inject(:+)
#=> "abc"
%w(a b c).sum
#=> TypeError: String can't be coerced into Integer
Also,
(1..1000000000).sum
#=> 500000000500000000 (execution time: less than 1s)
(1..1000000000).inject(:+)
#=> 500000000500000000 (execution time: upwards of a minute)
See this answer for more information on why sum is like this.
Array is empty, like if you want to return a Money object instead of an Integer, you can do something like array.sum( 0.to_money( "USD" ) ).%w(a b c).sum # => "abc"Array of Float or BigDecimal called by .sum will convert them back to Integer, breaking your math! You need to call foo.sum.to_f to preserve the Float.Just for the sake of diversity, you can also do this if your array is not an array of numbers, but rather an array of objects that have properties that are numbers (e.g. amount):
array.inject(0){|sum,x| sum + x.amount}
array.map(&:amount).inject(0, :+). See other answers.map then inject requires you to loop through the array twice: once to create a new array, the other to sum the members. This method is slightly more verbose, but also more efficient.array.sum(&:amount) as of today (using ruby >= 2.4 or rails).ruby 1.8.7 way is the following:
array.inject(0, &:+)
Ruby 2.4.0 is released, and it has an Enumerable#sum method. So you can do
array.sum
Examples from the docs:
{ 1 => 10, 2 => 20 }.sum {|k, v| k * v } #=> 50
(1..10).sum #=> 55
(1..10).sum {|v| v * 2 } #=> 110
Also allows for [1,2].sum{|x| x * 2 } == 6:
# http://madeofcode.com/posts/74-ruby-core-extension-array-sum
class Array
def sum(method = nil, &block)
if block_given?
raise ArgumentError, "You cannot pass a block and a method!" if method
inject(0) { |sum, i| sum + yield(i) }
elsif method
inject(0) { |sum, i| sum + i.send(method) }
else
inject(0) { |sum, i| sum + i }
end
end
end
Method 1:
[1] pry(main)> [1,2,3,4].sum
=> 10
[2] pry(main)> [].sum
=> 0
[3] pry(main)> [1,2,3,5,nil].sum
TypeError: nil can't be coerced into Integer
Method 2:
[24] pry(main)> [].inject(:+)
=> nil
[25] pry(main)> [].inject(0, :+)
=> 0
[4] pry(main)> [1,2,3,4,5].inject(0, :+)
=> 15
[5] pry(main)> [1,2,3,4,nil].inject(0, :+)
TypeError: nil can't be coerced into Integer
from (pry):5:in `+'
Method 3:
[6] pry(main)> [1,2,3].reduce(:+)
=> 6
[9] pry(main)> [].reduce(:+)
=> nil
[7] pry(main)> [1,2,nil].reduce(:+)
TypeError: nil can't be coerced into Integer
from (pry):7:in `+'
Method 4: When Array contains an nil and empty values, by default if you use any above functions reduce, sum, inject everything will through the
TypeError: nil can't be coerced into Integer
You can overcome this by,
[16] pry(main)> sum = 0
=> 0
[17] pry(main)> [1,2,3,4,nil, ''].each{|a| sum+= a.to_i }
=> [1, 2, 3, 4, nil, ""]
[18] pry(main)> sum
=> 10
Method 6: eval
Evaluates the Ruby expression(s) in string.
[26] pry(main)> a = [1,3,4,5]
=> [1, 3, 4, 5]
[27] pry(main)> eval a.join '+'
=> 13
[30] pry(main)> a = [1,3,4,5, nil]
=> [1, 3, 4, 5, nil]
[31] pry(main)> eval a.join '+'
SyntaxError: (eval):1: syntax error, unexpected end-of-input
1+3+4+5+
reduce and inject are literally the same thing. If you really want to build an object by yourself, you should consider each_with_object instead of each (Method 4). Suggestion: [1,2,3,4,nil, ''].each_with_object(0) {|a| sum+= a.to_i }You can also do it in easy way
def sum(numbers)
return 0 if numbers.length < 1
result = 0
numbers.each { |num| result += num }
result
end
inject or sum are preferred.You can use .map and .sum like:
array.map { |e| e }.sum
array.sum
array.sumArrayis empty, like if you want to return aMoneyobject instead of anInteger, you can do something likearray.sum( 0.to_money( "USD" ) ).