基于重复项对2D数组元素求和的最简单方法是什么?

时间:2016-10-04 20:06:55

标签: ruby-on-rails arrays ruby

所以说我有一个2D数组。也许是这样的:

data = [['oct 1', 4], ['oct 2', 5], ['oct 3', 9], ['oct 1', 2]]

我希望从中获取一个新数组,删除重复值(例如,oct 1),但总结相应的值。

所以我最终会:

data = (['oct 1', 6], ['oct 2', 5], ['oct 3', 9])

我可以想到一些方法来做到这一点,但它们看起来非常无穷无尽,而且通常会有一些疯狂的红宝石代码可以做几行代码,任何建议?

2 个答案:

答案 0 :(得分:2)

以下是两种方法,我的偏好是第一种。

使用计算哈希

代码

def combine(data)
  data.each_with_object(Hash.new(0)) { |(date, val), h| h[date] += val }.to_a
end

请参阅Hash::new,特别是对默认值的讨论。

实施例

data = [['oct 1', 4], ['oct 2', 5], ['oct 3', 9], ['oct 1', 2]]
combine data 
  #=> [["oct 1", 6], ["oct 2", 5], ["oct 3", 9]]

说明

enum = data.each_with_object(Hash.new(0))
  #=> #<Enumerator: [["oct 1", 4], ["oct 2", 5], ["oct 3", 9],
  #                  ["oct 1", 2]]:each_with_object({})> 

我们可以通过将它转换为数组来查看此枚举器生成的值。

enum.to_a
  #=> [[["oct 1", 4], {}], [["oct 2", 5], {}], [["oct 3", 9], {}],
  #    [["oct 1", 2], {}]]

enum的第一个值传递给块,并使用消歧并行分配计算块变量的值。

(date, val), h = enum.next
  #=> [["oct 1", 4], {}] 
date
  #=> "oct 1"      
val
  #=> 4

h[date] += val
  #=> h[date] = h[date] + val
  #=> h["oct 1"] = h["oct 1"] + 4
  #=> h["oct 1"] = 0 + 4 (no key "oct 1" so default value of `0` used)
  #=> h["oct 1"] = 4 

现在,

h #=> {"oct 1"=>4} 

enum的其余三个值传递给块,并执行块计算。

(date, val), h = enum.next
  #=> [["oct 2", 5], {"oct 1"=>4}]
h[date] += val 
  #=> 5 (the default value of `0` is again used)
h #=> {"oct 1"=>4, "oct 2"=>5} 

(date, val), h = enum.next
  #=> [["oct 3", 9], {"oct 1"=>4, "oct 2"=>5}] 
h[date] += val
  #=> 9 (the default value of `0` is again used)
h #=> {"oct 1"=>4, "oct 2"=>5, "oct 3"=>9} 

(date, val), h = enum.next
  #=> [["oct 1", 2], {"oct 1"=>4, "oct 2"=>5, "oct 3"=>9}] 
h[date] += val
  #=> 6 
h #=> {"oct 1"=>6, "oct 2"=>5, "oct 3"=>9} 

在上一次计算中,未使用默认值,因为哈希h已经有一个键"oct 1"

h[date] += val
  #=> h[date] = h[date] + val
  #=> h["oct 1"] = h["oct 1"] + 2
  #=> h["oct 1"] = 4 + 2

最后,

h.to_a
  #=> [["oct 1", 6], ["oct 2", 5], ["oct 3", 9]]

使用Enumerable#group_by

代码

def combine(data) 
  data.group_by(&:first).map { |date, vals| [date, vals.map(&:last).reduce(:+)] }
end

实施例

combine data 
  #=> [["oct 1", 6], ["oct 2", 5], ["oct 3", 9]]

说明

步骤:

h = data.group_by(&:first)
  #=> {"oct 1"=>[["oct 1", 4], ["oct 1", 2]],
  #    "oct 2"=>[["oct 2", 5]], "oct 3"=>[["oct 3", 9]]} 

h的第一个键值对传递给块:

date, vals = h.first
  #=> ["oct 1", [["oct 1", 4], ["oct 1", 2]]] 
date
  #=> "oct 1" 
vals
  #=> [["oct 1", 4], ["oct 1", 2]] 

并执行块计算。

a = vals.map(&:last)
  #=> [4, 2] 
t = a.reduce(:+)
  #=> 6 

因此h的第一个键值对映射到

[date, t]
  #=> ["oct 1", 6]

其余的计算方法类似。

答案 1 :(得分:1)

请尝试使用msg方法:

inject