Ruby uniq value and sum



我有数组

arr =  [
  ["BEER", 37],
  ["BEER", 95],
  ["BEER", 85],
  ["BEER", 60],
  ["BEER", 36],
  ["FOOD", 31],
  ["FOOD", 86],
  ["FOOD", 50],
  ["FOOD", 0]
]

如何获取类似的数组

new_arr = [ [ 'FOOD', sum_of_food ],
            ['BEER', sum_of_beer] ]

我刚刚通过Model.all.map {|c| [c.source, c.amount ] } 得到arr

您可以使用Enumerable#inject循环集合中的每个项目,并将池计算到用作累加器的新数组中。

arr =  [
 ["BEER", 37],
 ["BEER", 95],
 ["BEER", 85],
 ["BEER", 60],
 ["BEER", 36],
 ["FOOD", 31],
 ["FOOD", 86],
 ["FOOD", 50],
 ["FOOD", 0],
]
arr.inject({}) do |accumulator, (what, total)|
  accumulator[what] ||= 0
  accumulator[what]  += total
  accumulator
end

如果需要Array而不是Hash,只需对结果调用to_a即可。


如果数据存储在数据库中,则可以使用SQL SUM()语句直接查询数据库。

results = Model.select('source, SUM(amount) AS total').group(:source)
result = results.first
result.source
# => FOO
result.total
# => the sum of amount for FOO

您可以使用三种可枚举方法:group_bymapreduce(也称为inject):

arr.group_by(&:first).map { |k,v| [k, v.map(&:last).reduce(:+)] }
  #=> [["BEER", 313],["FOOD", 167]]

步骤:

h = arr.group_by(&:first)
  # => {"BEER"=>[["BEER", 37], ["BEER", 95], ["BEER", 85], ["BEER", 60],
  #              ["BEER", 36]],
  #     "FOOD"=>[["FOOD", 31], ["FOOD", 86], ["FOOD", 50], ["FOOD", 0]]} 

map将块变量分配给h:的第一个1元素(键值对)

k = "BEER"
v = [["BEER", 37], ["BEER", 95], ["BEER", 85], ["BEER", 60], ["BEER", 36]]

然后执行块计算:

[k, v.map(&:last).reduce(:+)]
  #=> ["BEER", [37, 95, 85, 60, 36].reduce(:+)]
  #   ["BEER", 313]

h的第二个元素被传递到块:

k = "FOOD"
v = [["FOOD", 31], ["FOOD", 86], ["FOOD", 50], ["FOOD", 0]]
[k, v.map(&:last).reduce(:+)]
  #=> ["FOOD", [31, 86, 50, 0].reduce(:+)]
  #   ["FOOD", 167]

如果已知arr的对由对的第一个元素分组,如示例中所示,我们可以使用Enumerable#chunk:

arr.chunk(&:first).map { |k,v| [k, v.map(&:last).reduce(:+)] }

1由于Ruby v1.9哈希密钥是按插入顺序排列的。因此,可以引用散列的第n个元素(键值对)。在v1.9之前,没有哈希密钥排序的概念。对于这些版本,上面的代码运行良好,但我们不知道map传递到其块的键值对的顺序

最新更新