0

I have the following item.json file

{
    "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",
      }
    ]
}

There are two items named LEGO and I want to get output for the total number of stock for the individual brand.

In ruby file item.rb i have code like:

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

I got output for stock no individually for each brand wherein I need the stock to be summed for two brand name "LEGO" displayed as one. Anyone has solution for this?

2 Answers 2

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}

It works like this:

  • Enumerable#group_by takes the array of items and creates a hash mapping the brand name to an array of all item hashes with that brand
  • Enumerable#map turns each brand/array pair in that hash into an array of the brand (unchanged) followed by:
  • Array#to_h then turns that array of two-value arrays into a hash, mapping the brand to the sum of stock values.

If you want simpler code that's less functional and possibly easier to understand:

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}
Sign up to request clarification or add additional context in comments.

Comments

1

To see what your JSON string looks like, let's create it from your hash, which I've denoted 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\"}]}"

After reading that from a file, into the variable j, we can now parse it to obtain the value of "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"}]

One way to obtain the desired tallies is to use a counting hash:

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) creates an empty hash (represented by the block variable h) with with a default value of zero1. That means that h[k] returns zero if the hash does not have a key k.

For the first element of arr (represented by the block variable g) we have:

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

Within the block, therefore, the calculation is:

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

Initially h has no keys, so h["LEGO"] returns the default value of zero, resulting in { "LEGO"=>55 } being merged into the hash h. As h now has a key "LEGO", h["LEGO"], will not return the default value in subsequent calculations.

Another approach is to use the form of Hash#update (aka merge!) that employs a block to determine the values of keys that are present in both hashes being merged:

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

1 k=>v is shorthand for { k=>v } when it appears as a method's argument.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.