数组内散列值之和 In ruby

Sum of hash value inside the array In ruby

我有这样的数组

data = 
  [
    { :lineItemKey=>57, :sku=>"12GA-RIO",  
      :name=>"12 Gauge", :quantity=>4, :unitPrice=>5.76 },
    { :lineItemKey=>168, :sku=>"22LR-CAS-40-CPRN-50", 
      :name=>"22 Long", :quantity=>2, :unitPrice=>7.6 },
    { :lineItemKey=>57, :sku=>"12GA-RIO", 
      :name=>"12 Gauge", :quantity=>1, :unitPrice=>5.76 }, 
    { :lineItemKey=>236, :sku=>"300BLK-UNWD-CTC-220-BTHP-20",
      :name=>"300 BLK", :quantity=>1, :unitPrice=>31.2 }
  ]

如果 sku 相同,我需要对数量求和并删除重复项。所以输出应该是这样的:

data = 
  [
    { :lineItemKey=>57, :sku=>"12GA-RIO", 
      :name=>"12 Gauge", :quantity=>5, :unitPrice=>5.76 },
    { :lineItemKey=>168, :sku=>"22LR-CAS-40-CPRN-50", 
      :name=>"22 Long", :quantity=>2, :unitPrice=>7.6 }, 
    { :lineItemKey=>236, :sku=>"300BLK-UNWD-CTC-220-BTHP-20",
      :name=>"300 BLK", :quantity=>1, :unitPrice=>31.2 }
  ]

输入

data = 
  [
    { :lineItemKey=>57, :sku=>"12GA-RIO", 
      :name=>"12 Gauge", :quantity=>4, :unitPrice=>5.76 },
    { :lineItemKey=>168, :sku=>"22LR-CAS-40-CPRN-50", 
      :name=>"22 Long", :quantity=>2, :unitPrice=>7.6 },
    { :lineItemKey=>57, :sku=>"12GA-RIO", 
      :name=>"12 Gauge", :quantity=>1, :unitPrice=>5.76 }, 
    { :lineItemKey=>236, :sku=>"300BLK-UNWD-CTC-220-BTHP-20",
      :name=>"300 BLK", :quantity=>1, :unitPrice=>31.2 }
  ]

代码

p data.group_by { |x| x[:lineItemKey] }
      .values
      .map { |arr| arr.reduce { |h1, h2| h1.merge(h2) { |k, oldv, newv| k.eql?(:quantity) ? oldv += newv : oldv } } }

输出

[
  { :lineItemKey=>57, :sku=>"12GA-RIO", 
    :name=>"12 Gauge", :quantity=>5, :unitPrice=>5.76 }, 
  { :lineItemKey=>168, :sku=>"22LR-CAS-40-CPRN-50",  
    :name=>"22 Long", :quantity=>2, :unitPrice=>7.6 }, 
  { :lineItemKey=>236, :sku=>"300BLK-UNWD-CTC-220-BTHP-20", 
    :name=>"300 BLK", :quantity=>1, :unitPrice=>31.2 }
]
data.each_with_object({}) do |g,h|
  h.update(g[:lineItemKey]=>g) do |_k, old_hash, new_hash|
    old_hash.merge(quantity: old_hash[:quantity] + new_hash[:quantity])
  end
end.values
  #=> [
  # {:lineItemKey=>57, :sku=>"12GA-RIO", :name=>"12 Gauge",
  #  :quantity=>5, :unitPrice=>5.76},
  # {:lineItemKey=>168, :sku=>"22LR-CAS-40-CPRN-50", :name=>"22 Long",
  #  :quantity=>2, :unitPrice=>7.6},
  # {:lineItemKey=>236, :sku=>"300BLK-UNWD-CTC-220-BTHP-20",
  #  :name=>"300 BLK", :quantity=>1, :unitPrice=>31.2}
  # ]

注意values的接收者是

{
   57=>{:lineItemKey=>57, :sku=>"12GA-RIO", :name=>"12 Gauge",
        :quantity=>5, :unitPrice=>5.76},
  168=>{:lineItemKey=>168, :sku=>"22LR-CAS-40-CPRN-50",
        :name=>"22 Long", :quantity=>2, :unitPrice=>7.6},
  236=>{:lineItemKey=>236, :sku=>"300BLK-UNWD-CTC-220-BTHP-20",
        :name=>"300 BLK", :quantity=>1, :unitPrice=>31.2}
}

这使用 Hash#update (a.k.a merge!) 的形式,它使用一个块来计算合并的两个散列中存在的键的值。这个块是

do |_k, old_hash, new_hash|
  old_hash.merge(quantity: old_hash[:quantity] + new_hash[:quantity])
end

有关三个块变量的定义,请参阅文档。 k(公共键)之前的下划线是为了向 reader 发出信号,表明它不用于块计算(一种常见约定,通常只是单独的下划线)。