Как суммировать свойства объектов в массиве в Ruby

Я понимаю, что для суммирования элементов массива в Ruby можно использовать метод inject, т. е.

array = [1,2,3,4,5];
puts array.inject(0, &:+) 

но как суммировать свойства объектов в массиве объектов, например?

существует массив объектов, и каждый объект имеет свойство "cash", например. Поэтому я хочу суммировать их денежные остатки в один итог. Что-то вроде того...

array.cash.inject(0, &:+) # (but this doesn't work)

Я понимаю, что, вероятно, мог бы сделать новый массив, состоящий только из наличных денег собственности и суммировать это, но я ищите более чистый метод, если это возможно!

7 ответов


array.map(&:cash).inject(0, &:+)

или

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

вы также можете попробовать:

array.sum(&:cash)

его ярлык для бизнеса inject и кажется более читаемым для меня.
http://api.rubyonrails.org/classes/Enumerable.html


#reduce принимает блок (тег &:+ - это ярлык для создания proc / block, который делает +). Это один из способов сделать то, что вы хотите:

array.reduce(0) { |sum, obj| sum + obj.cash }

самым кратким образом:

array.map(&:cash).sum

Если результирующий массив на карте имеет нулевые элементы:

array.map(&:cash).compact.sum

никакая потребность использовать начальное в впрыскивает и плюс деятельность может быть короче

array.map(&:cash).inject(:+)

вот некоторые интересные показатели

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

и результаты

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

Как вы можете видеть, впрысните немного быстрее


если начальное значение для суммирования равно 0, то только сумма идентична inject:

array.map(&:cash).sum

и я бы предпочел версию блока:

array.sum { |a| a.cash }

потому что Proc от символа часто слишком ограничен (без параметров и т. д.).

(Должен Active_Support)