你如何在ruby中应用多个过滤器和总和?

时间:2015-02-22 04:30:37

标签: ruby

我有一个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}

1 个答案:

答案 0 :(得分:2)

如果

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: [[: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} 

使用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}