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

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

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

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

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

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

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

Ответ 1

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

или

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

Ответ 2

В Ruby On Rails вы также можете попробовать:

array.sum(&:cash)

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

Ответ 3

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

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

Ответ 4

Самый сжатый способ:

array.map(&:cash).sum

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

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

Ответ 5

Нет необходимости использовать начальную инъекцию, а плюс операция может быть короче

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

Ответ 6

Вот некоторые интересные тесты

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

Как вы можете заметить, немного ускоряется

Ответ 7

Если начальное значение для суммирования равно 0, то сумма сама по себе идентична инъекции:

array.map(&:cash).sum

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

array.sum { |a| a.cash }

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

(требуется Active_Support)