我有一个Order
类型的对象数组Order
:status
:amount
状态可以预付费,后付费和已取消
我想获得每种状态的金额总和。你是如何用红宝石做到的?
实施例: 如果我有五个订单
O1 - postpaid - 100
O2 - prepaid - 10
O3 - prepaid - 15
O4 - cancelled - 20
O5 - cancelled - 45
输出应该是一个看起来像这样的地图
{postpaid: 100, prepaid: 25, cancelled: 65}
答案 0 :(得分:2)
如果
a = [[:post, 100],[:pre, 10], [:pre, 15],[:canc, 20],[:canc, 45]]
有很多人想要获得总和。这是两个。
该对象是一个哈希值,使用默认值零进行初始化:
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: [[:post, 100], [:pre, 10], [:pre, 15],
# [:canc, 20], [:canc, 45]]:each_with_object({})>
我们可以通过将枚举器转换为数组来查看由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}
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}