1

我有 3 个哈希数组:

a = [{name: 'Identifier', value: 500}, {name: 'Identifier2', value: 50 }]
b = [{name: 'Identifier', value: 500}, {name: 'Identifier2', value: 50 }]
c = [{name: 'Identifier', value: 500}, {name: 'Identifier2', value: 50 }]

name我必须根据每个标识符的属性将它们合并为一个,所以结果将是:

d = [{name: 'Identifier', value: 1500 }, {name: 'Identifier2', value: 150}]

有没有一种聪明的红宝石方法可以做到这一点,还是我必须创建另一个散列,其中键是标识符,值是值,然后将其转换为数组?

谢谢你。

4

4 回答 4

2

当要汇总哈希集合中单个键的值时,我通常从构造一个计数哈希开始:

h = (a+b+c).each_with_object({}) do |g,h|
  h[g[:name]] = (h[g[:name]] || 0) + g[:value]
end
  #=> {"Identifier"=>1500, "Identifier2"=>150}

请注意,如果h没有键g[:name], h[g[:name]] #=> nil,则:

h[g[:name]] = (h[g[:name]] || 0) + g[:value]
            = (nil || 0) + g[:value]
            = 0 + g[:value]
            = g[:value]

我们现在可以很容易地得到想要的结果:

h.map { |(name,value)| { name: name, value: value } }
  #=> [{:name=>"Identifier", :value=>1500},
  #    {:name=>"Identifier2", :value=>150}] 

如果需要,可以链接这两个表达式:

(a+b+c).each_with_object({}) do |g,h|
  h[g[:name]] = (h[g[:name]] || 0) + g[:value]
end.map { |(name,value)| { name: name, value: value } }
  #=> [{:name=>"Identifier", :value=>1500},
  #    {:name=>"Identifier2", :value=>150}] 

有时您可能会看到:

h[k1] = (h[k1] || 0) + g[k2]

书面:

(h[k1] ||= 0) + g[k2]

它扩展到同样的事情。

下面是另一种计算方法h,我会说它更像“Ruby-like”。

h = (a+b+c).each_with_object(Hash.new(0)) do |g,h|
  h[g[:name]] += g[:value]
end

这将使用Hash::newh的形式创建由块变量表示的哈希,该形式接受一个称为默认值的参数:

h = Hash.new(0)

这意味着如果h没有 key kh[k]则返回默认值,here 0。注意

h[g[:name]] += g[:value]

扩展为:

h[g[:name]] = h[g[:name]] + g[:value]

因此,如果h没有密钥g[:name],则减少为:

h[g[:name]] = 0 + g[:value]

如果你想知道为什么h[g[:name]]等式左边没有被替换0,那是因为表达式的那部分使用了方法Hash#[]=,而右边使用了方法Hash#[] 。Hash::new只关注默认值Hash#[]

于 2020-08-26T19:47:28.200 回答
0

我假设所有数组中标识符的顺序是相同的。这{name: 'Identifier', value: ...}始终是所有 3 个数组中的第一个元素,{name: 'Identifier2', value: ... }始终是第二个元素,依此类推。在这个简单的情况下,简单each_with_index是一个简单明了的解决方案:

d = []

a.each_with_index do |hash, idx|
  d[idx] = {name: hash[:name], value: a[idx][:value] + b[idx][:value] + c[idx][:value] }
end

# Or a more clear version using map:

a.each_with_index do |hash, idx|
  d[idx] = {name: hash[:name], value: [a, b, c].map { |h| h[idx][:value] }.sum }
end
于 2020-08-26T16:38:37.610 回答
0

几种不同的方法,避免任何挑剔的数组索引等,(在功能上也是如此,因为你已经添加了标签):

grouped = (a + b + c).group_by { _1[:name] }
name_sums = grouped.transform_values { |hashes| hashes.map { _1[:value] }.sum }
name_vals = (a + b + c).map { Hash[*_1.values_at(:name, :value)] }
name_sums = name_vals.reduce { |l, r| l.merge(r) { |k, lval, rval| lval + rval } }

在任何一种情况下,完成它:

name_sums.map { |name, value| { name: name, value: value } }
于 2020-08-26T17:31:15.310 回答
0

你可以用 ruby​​ 做任何事情!

这是您的问题的解决方案:

d = (a+b+c).group_by { |e| e[:name] }.map { |f| f[1][0].merge(value: f[1].sum { |g| g[:value] }) }

我鼓励您查看 Array Ruby 文档以获取更多信息:https ://ruby-doc.org/core-2.7.0/Array.html

于 2020-08-26T16:34:23.120 回答