How to sum properties of the objects within an array in Ruby
RubyArraysObjectSumInjectRuby Problem Overview
I understand that in order to sum array elements in Ruby one can use the inject method, i.e.
array = [1,2,3,4,5];
puts array.inject(0, &:+)
But how do I sum the properties of objects within an object array e.g.?
There's an array of objects and each object has a property "cash" for example. So I want to sum their cash balances into one total. Something like...
array.cash.inject(0, &:+) # (but this doesn't work)
I realise I could probably make a new array composed only of the property cash and sum this, but I'm looking for a cleaner method if possible!
Ruby Solutions
Solution 1 - Ruby
array.map(&:cash).inject(0, &:+)
or
array.inject(0){|sum,e| sum + e.cash }
Solution 2 - Ruby
In Ruby On Rails you might also try:
array.sum(&:cash)
Its a shortcut for the inject business and seems more readable to me.
http://api.rubyonrails.org/classes/Enumerable.html
Solution 3 - Ruby
#reduce
takes a block (the &:+
is a shortcut to create a proc/block that does +
). This is one way of doing what you want:
array.reduce(0) { |sum, obj| sum + obj.cash }
Solution 4 - Ruby
Most concise way:
array.map(&:cash).sum
If the resulting array from the map has nil items:
array.map(&:cash).compact.sum
Solution 5 - Ruby
If start value for the summation is 0, then sum alone is identical to inject:
array.map(&:cash).sum
And I would prefer the block version:
array.sum { |a| a.cash }
Because the Proc from symbol is often too limited (no parameters, etc.).
(Needs Active_Support)
Solution 6 - Ruby
Here some interesting benchmarks
array = Array.new(1000) { OpenStruct.new(property: rand(1000)) }
Benchmark.ips do |x|
x.report('map.sum') { array.map(&:property).sum }
x.report('inject(0)') { array.inject(0) { |sum, x| sum + x.property } }
x.compare!
end
And results
Calculating -------------------------------------
map.sum 249.000 i/100ms
inject(0) 268.000 i/100ms
-------------------------------------------------
map.sum 2.947k (± 5.1%) i/s - 14.691k
inject(0) 3.089k (± 5.4%) i/s - 15.544k
Comparison:
inject(0): 3088.9 i/s
map.sum: 2947.5 i/s - 1.05x slower
As you can see inject a little bit faster
Solution 7 - Ruby
There's no need to use initial in inject and plus operation can be shorter
array.map(&:cash).inject(:+)