53

我知道为了在 Ruby 中对数组元素求和,可以使用注入方法,即

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

但是我如何总结对象数组中对象的属性,例如?

例如,有一个对象数组,每个对象都有一个属性“现金”。所以我想把他们的现金余额加起来。就像是...

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

我意识到我可能会创建一个仅由属性 cash 组成的新数组并将其相加,但如果可能的话,我正在寻找一种更清洁的方法!

4

7 回答 7

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

或者

array.inject(0){|sum,e| sum + e.cash }
于 2012-06-30T09:13:57.343 回答
58

在 Ruby On Rails 中,您还可以尝试:

array.sum(&:cash)

它是注入业务的捷径,对我来说似乎更具可读性。
http://api.rubyonrails.org/classes/Enumerable.html

于 2013-12-17T21:01:54.420 回答
11

#reduce需要一个块(这&:+是创建 proc/block 的快捷方式+)。这是做你想做的事情的一种方式:

array.reduce(0) { |sum, obj| sum + obj.cash }
于 2012-06-30T09:15:14.050 回答
4

最简洁的方式:

array.map(&:cash).sum

如果地图的结果数组包含 nil 项:

array.map(&:cash).compact.sum
于 2015-11-02T20:47:34.690 回答
2

如果求和的起始值为 0,则单独的 sum 与注入相同:

array.map(&:cash).sum

我更喜欢块版本:

array.sum { |a| a.cash }

因为 Proc from 符号通常太有限(没有参数等)。

(需要 Active_Support)

于 2016-08-08T00:25:16.583 回答
2

这里有一些有趣的基准

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

如您所见,注入速度更快

于 2016-12-12T18:17:59.683 回答
1

不需要在inject中使用initial并且plus操作可以更短

array.map(&:cash).inject(:+)
于 2012-06-30T10:58:29.867 回答