How to sum properties of the objects within an array in Ruby

RubyArraysObjectSumInject

Ruby 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.947k5.1%) i/s -     14.691k
           inject(0)      3.089k5.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(:+)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSpike FitschView Question on Stackoverflow
Solution 1 - RubyYuri BarbashovView Answer on Stackoverflow
Solution 2 - RubyjacklinView Answer on Stackoverflow
Solution 3 - RubyTheoView Answer on Stackoverflow
Solution 4 - RubyvicocasView Answer on Stackoverflow
Solution 5 - RubyChristopher OezbekView Answer on Stackoverflow
Solution 6 - RubympospelovView Answer on Stackoverflow
Solution 7 - RubymegasView Answer on Stackoverflow