使用ruby

时间:2016-04-06 02:58:51

标签: ruby json merge duplicates

我有以下 item.json 文件

{
    "items": [
      {
        "brand": "LEGO",
        "stock": 55,
        "full-price": "22.99",
      },
      {
        "brand": "Nano Blocks",
        "stock": 12,
        "full-price": "49.99",
      },
      {
        "brand": "LEGO",
        "stock": 5,
        "full-price": "199.99",
      }
    ]
}

有两个名为LEGO的项目,我想获得个别品牌的总库存量的输出。

在ruby文件 item.rb 中,我的代码如下:

require 'json'

path = File.join(File.dirname(__FILE__), '../data/products.json')
file = File.read(path)
products_hash = JSON.parse(file)

products_hash["items"].each do |brand|
puts "Stock no: #{brand["stock"]}"
end

我为每个品牌单独输出了库存,我需要将两个品牌名称“LEGO”的库存汇总为一个。 有人有解决方案吗?

2 个答案:

答案 0 :(得分:1)

json = File.open(path,'r:utf-8',&:read) # in case the JSON uses UTF-8
items = JSON.parse(json)['items']
stock_by_brand = items
  .group_by{ |h| h['brand'] }
  .map do |brand,array|
    [ brand,
      array
        .map{ |item| item['stock'] }
        .inject(:+) ]
  end
  .to_h
#=> {"LEGO"=>60, "Nano Blocks"=>12}

它的工作原理如下:

  • Enumerable#group_by获取项目数组并创建哈希,将品牌名称映射到具有该品牌的所有item哈希数组
  • Enumerable#map将该哈希中的每个品牌/数组对转换为品牌数组(未更改),然后执行以下操作:
  • Array#to_h然后将该两个值数组的数组转换为哈希值,将品牌映射到stock值的总和。

如果您想要更简单的代码 functional 并且可能更容易理解:

stock_by_brand = {}  # an empty hash
items.each do |item|
  stock_by_brand[ item['brand'] ] ||= 0 # initialize to zero if unset
  stock_by_brand[ item['brand'] ] += item['stock']
end
p stock_by_brand     #=> {"LEGO"=>60, "Nano Blocks"=>12}

答案 1 :(得分:1)

要查看您的JSON字符串的外观,请使用您的哈希创建它,我已将其表示为h

require 'json'

j = JSON.generate(h)
  #=> "{\"items\":[{\"brand\":\"LEGO\",\"stock\":55,\"full-price\":\"22.99\"},{\"brand\":\"Nano Blocks\",\"stock\":12,\"full-price\":\"49.99\"},{\"brand\":\"LEGO\",\"stock\":5,\"full-price\":\"199.99\"}]}"

从文件读取变量j之后,我们现在可以解析它以获取"items"的值:

arr = JSON.parse(j)["items"]
  #=> [{"brand"=>"LEGO", "stock"=>55, "full-price"=>"22.99"},
  #    {"brand"=>"Nano Blocks", "stock"=>12, "full-price"=>"49.99"},
  #    {"brand"=>"LEGO", "stock"=>5, "full-price"=>"199.99"}]

获得所需结果的一种方法是使用计数哈希:

arr.each_with_object(Hash.new(0)) {|g,h| h.update(g["brand"]=>h[g["brand"]]+g["stock"])}
  #=> {"LEGO"=>60, "Nano Blocks"=>12} 

Hash.new(0)创建一个空哈希(由块变量h表示),默认值为零 1 。这意味着如果哈希没有键h[k]k将返回零。

对于arr的第一个元素(由块变量g表示),我们有:

g["brand"] #=> "LEGO"
g["stock"] #=> 55

因此,在该区块中,计算为:

g["brand"] => h[g["brand"]]+g["stock"]
  #=> "LEGO" => h["LEGO"] + 55

最初h没有密钥,因此h["LEGO"]会返回默认值零,从而导致{ "LEGO"=>55 }合并到哈希h中。由于h现在有一个键"LEGO"h["LEGO"],在后续计算中不会返回默认值。

另一种方法是使用Hash#update(aka merge!)的形式,它使用一个块来确定合并的两个哈希中存在的键的值:

arr.each_with_object({}) {|g,h| h.update(g["brand"]=>g["stock"]) {|_,o,n| o+n}}
  #=> {"LEGO"=>60, "Nano Blocks"=>12} 

1 k=>v{ k=>v }作为方法论证时的简写。