如何对数组的元素求和?

时间:2014-10-20 11:08:12

标签: ruby-on-rails ruby ruby-on-rails-4

我的问题是两个折叠。首先,我有一个未规范化的表,其中包含3对数据(6列),这些数据必须求和。它大致是这样的:

|-module_a-|-value_a-|-module_b-|-value_b-|-module_c-|-value_c-|
----------------------------------------------------------------
|   02     |    15   |    nil   |   nil   |   nil    |   nil   |
|   01     |    10   |    00    |   10    |   nil    |   nil   |
|   00     |    25   |    nil   |   nil   |   nil    |   nil   |
|   02     |    22   |    01    |   15    |   03     |   20    |
|   03     |    10   |    nil   |   nil   |   nil    |   nil   |
|   02     |     8   |    00    |   5     |   nil    |   nil   |

我的主要目标是获得每个模块的总和,但由于它们可以混合在3个字段中,因此通过activerecord(或者我认为)这样做变得复杂。

无论如何,为了清理它,我做了一个像这样的数组:

tok1=Office.select('module_a as module, value_a as value')
tok2=Office.select('module_b as module, value_b as value').where.not(:value_b => nil)
tok3=Office.select('module_c as module, value_c as value').where.not(:value_c => nil)

tok=(tok1+tok2+tok3)

结果:

|-module-|-value-|
------------------
|   02   |  15   |
|   01   |  10   |
|   00   |  25   |
|   02   |  22   |
|   03   |  10   |
|   02   |   8   |
|   00   |  10   |
|   01   |  15   |
|   03   |  20   |
|   00   |   5   |

到目前为止,非常好。

现在,我想对模块值进行总结。如果我可以通过ActiveRecord执行GROUP和SUM,这将非常简单,但结果变量tok是一个数组,因此不可能有ActiveRecord函数。如果我尝试通过tok=tok1.merge(tok2)合并它们,我最终只得到了tok2的结果,我认为这是因为我在查询Office表时重命名字段,我不确定。我尝试以不同的方式将它们组合在一起,但我得到的错误是关系不是不可变的。

无论如何,由于做tok=tok1+tok2+tok3的结果以数组结束,我想把它作为一个数组来处理。我尝试过使用map,在做了tok.map{|h| [h.module, h.value]}之后我最终得到了这样一个数组:

{[02, 15], [01, 10], [00,  25}, [02, 22], [03,10], [02, 8], [00,10], [01,15], [03,20], [00, 5]}

我不知道如何从这里开始。我试着把它变成一个哈希,但是我对map方法还是太新了所以我不知道怎么做。理想情况下,我认为,它应该是这样的:

{ ["02" => 15],["01" => 10],["00" => 25],["02" => 22],["03" => 10],["02" => 8],
  ["00" => 10],["01" => 15],["03" => 20],["00" => 5]}

我已经在其他问题中读过如何用这样的散列/数组来对这些值求和,所以如果我这样得到它,我想我会被设置。

对于凌乱的解释感到抱歉。

2 个答案:

答案 0 :(得分:3)

你可以这样做:

arr = [[02, 15], [01, 10], [00,  25], [02, 22], [03,10], [02, 8], [00,10], [01,15], [03,20], [00, 5]]
hash = {}
arr.each do |item|
  hash[item[0]] ||= 0
  hash[item[0]] += item[1]
end
hash # => {2=>45, 1=>25, 0=>40, 3=>30}

或没有上一张地图:

hash = {}
tok.each do |item|
  hash[item.module] ||= 0
  hash[item.module] += item.value
end

编辑:正如Dima Goltsman的评论中提到的那样(谢谢!)它可以使用注入以更短的形式重写:

arr = [[02, 15], [01, 10], [00,  25], [02, 22], [03,10], [02, 8], [00,10], [01,15], [03,20], [00, 5]]    
arr.inject(Hash.new(0)) do |hash, item|
  hash[item[0]] += item[1]
  hash
end # => {2=>45, 1=>25, 0=>40, 3=>30}

tok.inject(Hash.new(0)) do |hash, item|
  hash[item.module] += item.value
  hash
end

答案 1 :(得分:1)

数据库通常非常擅长分组和添加内容,因此我可能会在SQL中尽可能多地执行此操作。

tok = Office.find_by_sql <<-END_SQL
  SELECT module, SUM(value)
  FROM (
    SELECT module_a AS module, value_a AS value FROM offices
    UNION
    SELECT module_b AS module, value_b AS value FROM offices WHERE value_b IS NOT NULL
    UNION
    SELECT module_c AS module, value_c AS value FROM offices WHERE value_c IS NOT NULL
  ) AS modules(module, value)
  GROUP BY module
END_SQL

使用以下属性评估Office个实例的集合:

+--------+-------+
| module | value |
+--------+-------+
| 1      | 25    |
| 3      | 30    |
| 0      | 40    |
| 2      | 45    |
+--------+-------+

如果由我决定,我可能会使用给定的SQL查询创建一个视图,并在其上添加一个ActiveRecord数据模型。这样,Office模型不会用于除最初预期之外的目的。由于实现视图在DBMS和分支之间因原始主题而异,因此我不会在此详细介绍。