提问人:jkvithanage 提问时间:4/16/2023 最后编辑:jkvithanage 更新时间:4/17/2023 访问量:134
如何在 Ruby 中合并具有相同键的哈希数组
How to merge array of hashes with same keys in Ruby
问:
我有一组这样的项目:
items = [{name: 'item_1', qty: 1, unit_price: 5},
{name: 'item_1', qty: 2, unit_price: 5},
{name: 'item_2', qty: 3, unit_price: 10},
{name: 'item_2', qty: 2, unit_price: 10}]
我想通过对数量求和,将同名的项目合并为一个哈希值。上述示例的预期输出应为:
[{name: 'item_1', qty: 3, unit_price: 5},
{name: 'item_2', qty: 5, unit_price: 10}]
答:
1赞
marmeladze
4/16/2023
#1
您可以从这里开始,找到最佳解决方案
items.inject([]) do |acc, el|
item_exists = acc.any? {|e| e[:name].eql? el[:name] }
item_exists ? acc.find {|e| e[:name].eql? el[:name] }[:qty] += el[:qty] : acc << el
acc
end
2赞
Rajagopalan
4/16/2023
#2
输入
items = [
{name: 'item_1', qty: 1, unit_price: 5},
{name: 'item_1', qty: 2, unit_price: 5},
{name: 'item_2', qty: 3, unit_price: 10},
{name: 'item_2', qty: 2, unit_price: 10}
]
法典
result = items.group_by { |item| item[:name] }
.values
.map do |arr|
arr.reduce do |h1, h2|
h1.merge(h2) do |k1, v1, v2|
k1.eql?(:qty) ? v1 + v2 : v1
end
end
end
输出
[{:name=>"item_1", :qty=>3, :unit_price=>5}, {:name=>"item_2", :qty=>5, :unit_price=>10}]
2赞
Ben Stephens
4/16/2023
#3
这使用双 splat 运算符来复制哈希值,并在首次填充累加器哈希中名称的条目时将数量设置为零。
items.reduce({}) do |acc, val|
acc[val[:name]] ||= {**val, qty: 0} # initialise entry in accumulator hash for name
acc[val[:name]][:qty] += val[:qty] # add quantity to entry in accumulator hash
acc
end.values
评论
0赞
Cary Swoveland
4/18/2023
这太酷了!我从没想过双飞溅如此多才多艺。
3赞
Cary Swoveland
4/17/2023
#4
items = [
{name: 'item_1', qty: 1, unit_price: 5},
{name: 'item_1', qty: 2, unit_price: 5},
{name: 'item_2', qty: 3, unit_price: 10},
{name: 'item_2', qty: 2, unit_price: 10}
]
items.each_with_object({}) do |g,h|
h.update(g[:name]=>g) { |_k,o,n| o.merge(qty: o[:qty] + n[:qty]) }
end.values
[{:name=>"item_1", :qty=>3, :unit_price=>5},
{:name=>"item_2", :qty=>5, :unit_price=>10}]
这使用 Hash#update (aka ) 的形式,它使用块 () 来计算正在合并的两个哈希中存在的键的值。merge!
{ |_k,o,n| o.merge(qty: o[:qty] + n[:qty]) }
g[:name]=>g
,当它作为参数出现时(如此处),是 (并等同于) 的简写。包含公用键的块变量的名称以下划线开头,向下划线向读者发出信号,表明它未用于块计算。通常将此类块变量表示为简单(局部变量的有效名称)。{ g[:name]=>g }
_k
_
items
没有突变。
我们可以添加一些语句来检查示例的计算。puts
items.each_with_object({}) do |g,h|
puts "h = #{h}"
puts "g = #{g}"
puts "g[:name]=>g = #{g[:name]}=>#{g}"
h.update(g[:name]=>g) do |_k,o,n|
puts "Defer to block to calc value of key #{_k}"
puts "o = #{o}"
puts "n = #{n}"
puts "{ qty: o[:qty] + n[:qty] } = #{{ qty: o[:qty] + n[:qty] }}"
f = o.merge(qty: o[:qty] + n[:qty])
puts "o.merge(qty: o[:qty] + n[:qty]) = #{f}"
f
end
puts
end.tap do |h|
puts "Value of h returned = #{h}"
puts "h.values = #{h.values}"
end.
values
#=> [{:name=>"item_1", :qty=>3, :unit_price=>5},
# {:name=>"item_2", :qty=>5, :unit_price=>10}]
以下是打印的(经过一些手工重新格式化)。
h = {}
g = {:name=>"item_1", :qty=>1, :unit_price=>5}
g[:name]=>g = item_1=>{:name=>"item_1", :qty=>1, :unit_price=>5}
h = {"item_1"=>{:name=>"item_1", :qty=>1, :unit_price=>5}}
g = {:name=>"item_1", :qty=>2, :unit_price=>5}
g[:name]=>g = item_1=>{:name=>"item_1", :qty=>2, :unit_price=>5}
Defer to block to calc value of key item_1
o = {:name=>"item_1", :qty=>1, :unit_price=>5}
n = {:name=>"item_1", :qty=>2, :unit_price=>5}
{ qty: o[:qty] + n[:qty] } = {:qty=>3}
o.merge(qty: o[:qty] + n[:qty]) = {:name=>"item_1", :qty=>3, :unit_price=>5}
h = {"item_1"=>{:name=>"item_1", :qty=>3, :unit_price=>5}}
g = {:name=>"item_2", :qty=>3, :unit_price=>10}
g[:name]=>g = item_2=>{:name=>"item_2", :qty=>3, :unit_price=>10}
h = {"item_1"=>{:name=>"item_1", :qty=>3, :unit_price=>5},
"item_2"=>{:name=>"item_2", :qty=>3, :unit_price=>10}}
g = {:name=>"item_2", :qty=>2, :unit_price=>10}
g[:name]=>g = item_2=>{:name=>"item_2", :qty=>2, :unit_price=>10}
Defer to block to calc value of key item_2
o = {:name=>"item_2", :qty=>3, :unit_price=>10}
n = {:name=>"item_2", :qty=>2, :unit_price=>10}
{ qty: o[:qty] + n[:qty] } = {:qty=>5}
o.merge(qty: o[:qty] + n[:qty]) = {:name=>"item_2", :qty=>5, :unit_price=>10}
Value of h returned =
{"item_1"=>{:name=>"item_1", :qty=>3, :unit_price=>5},
"item_2"=>{:name=>"item_2", :qty=>5, :unit_price=>10}}
h.values = [{:name=>"item_1", :qty=>3, :unit_price=>5},
{:name=>"item_2", :qty=>5, :unit_price=>10}]
请注意,当 的第一个和第三个元素被传递到外部块时,不会参考 的块。那是因为 尚未包含密钥 .update
items
h
g[:name]
参见 Object#tap。
评论
1赞
Cary Swoveland
4/17/2023
@Ben,非常感谢您发现我的错误。注意它只是变异了,但它是错误的。我已经做了一个必要的修复,并将详细说明。顺便说一句,当我提供改变输入的解决方案时,OP对是否允许(我很少这样做)保持沉默,我遵循我认为是SO的一般做法,在我的回答中指出它改变了输入。items
0赞
jkvithanage
4/18/2023
感谢您提供更简单且解释清楚的解决方案。
评论
{name: 'item_2', qty: 3, unit_price: 7 }, {name: 'item_2', qty: 2, unit_price: 10}
{name: 'item_2', qty: 5, unit_price: 10}