你如何在ruby中应用多个filter和总和?
我有一个Order类型的对象数组
Order :status :amount
状态可以预付 , 后付和取消
我想获得每种状态的金额总和。 你是如何用ruby做到的?
示例:如果我有五个订单
O1 - postpaid - 100 O2 - prepaid - 10 O3 - prepaid - 15 O4 - cancelled - 20 O5 - cancelled - 45
输出应该是一个看起来像这样的地图
{postpaid: 100, prepaid: 25, cancelled: 65}
如果
a = [[:post, 100],[:pre, 10], [:pre, 15],[:canc, 20],[:canc, 45]]
有很多人想要获得这笔钱。 这是两个。
使用Enumerable#each_with_object
该对象是一个哈希值,使用默认值零进行初始化:
a.each_with_object(Hash.new(0)) { |(type,amt),h| h[type] += amt } #=> {:post=>100, :pre=>25, :canc=>65}
以下是步骤:
enum = a.each_with_object(Hash.new(0)) #=> #
我们可以通过将枚举器转换为数组来查看由Enumerator#each传递到块中的枚举器的内容:
enum.to_a #=> [[[:post, 100], {}], [[:pre, 10], {}], [[:pre, 15], {}], # [[:canc, 20], {}], [[:canc, 45], {}]]
我们可以使用Enumerator#next逐步完成计算:
(type,amt),h = enum.next #=> [[:post, 100], {}] type #=> :post amt #=> 100 h #=> {} h[type] += amt #=> h[type] = h[type] + amt #=> h[:post] = h[:post] + 100 #=> h[:post] = 0 + 100 # h[:post] on right set to default value #=> h[:post] = 100
(type,amt),h = enum.next #=> [[:pre, 10], {:post=>100}] h[type] += amt #=> 10 (type,amt),h = enum.next #=> [[:pre, 15], {:post=>100, :pre=>10}] h[type] += amt #=> 25 (type,amt),h = enum.next #=> [[:canc, 20], {:post=>100, :pre=>25}] h[type] += amt #=> 20 (type,amt),h = enum.next #=> [[:canc, 45], {:post=>100, :pre=>25, :canc=>20}] h[type] += amt #=> 65 h #=> {:post=>100, :pre=>25, :canc=>65}
使用Enumerable#group_by
Hash[a.group_by(&:first) .values .map { |b| [b.first.first, b.reduce(0) {|tot,(_,amt)| tot+amt}]}] #=> {:post=>100, :pre=>25, :canc=>65}
步骤:
h = a.group_by(&:first) #=> {:post=>[[:post, 100]], # :pre=> [[:pre, 10], [:pre, 15]], # :canc=>[[:canc, 20], [:canc, 45]]} c = h.values #=> [[[:post, 100]], # [[:pre, 10], [:pre, 15]], # [[:canc, 20], [:canc, 45]]]
map
首先将c
的第一个元素(包含一个元素的数组,数组[:post, 100]
)传递给块并将其分配给块变量:
b = [[:post, 100]] [b.first.first, b.reduce(0) {|tot,(_,amt)| tot+amt}] #=> [[:post, 100].first, [[:post, 100]].reduce(0) {|tot,(_,amt)| tot+amt}] #=> [:post, 100]
map
然后将c
的第二个元素传递给块:
b = [[:pre, 10], [:pre, 15]] [b.first.first, b.reduce(0) {|tot,(_,amt)| tot+amt}] #=> [:pre, 25]
等等。 在将c
所有元素传递给块之后,映射的值将作为数组返回:
d = [[:post, 100], [:pre, 25], [:canc, 65]]
最后一步是将其转换为哈希:
Hash[d] #=> {:post=>100, :pre=>25, :canc=>65}
或者在Ruby 2.0+中:
d.to_h #=> {:post=>100, :pre=>25, :canc=>65}