Ruby uniq值和总和

时间:2015-12-06 12:28:17

标签: ruby-on-rails arrays ruby

我有数组数组

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] ]

我刚刚arr

获得了Model.all.map {|c| [c.source, c.amount ] }

2 个答案:

答案 0 :(得分:3)

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

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而不是哈希,只需在结果上调用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

答案 1 :(得分:1)

您可以使用三种Enumerable方法: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传递给其块的键值对的顺序。