如何在Ruby中合并具有相同键的哈希数组

fkaflof6  于 2023-04-20  发布在  Ruby
关注(0)|答案(4)|浏览(158)

我有一个这样的项目数组:

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

pcrecxhr1#

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(又名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}]

请注意,当items的第一个和第三个元素传递到外部块时,不会查询update的块,这是因为h还不包含键g[:name]
请参见对象#tap。

pdkcd3nj

pdkcd3nj2#

输入

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

nhn9ugyo3#

这使用双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
w1e3prcc

w1e3prcc4#

你可以从这里开始,找到一个最优解

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

相关问题