0

I have array like this

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

I need to sum the quantity if the sku is same and remove the duplicate. so the output should be like:

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

2 Answers 2

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

Note that the receiver of values is

{
   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}
}

This uses the form of Hash#update (a.k.a merge!) that employs a block to compute the values of keys that are present in both hashes being merged. Here that block is

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

See the doc for definitions of the three block variables. The underscore preceding k (the common key) is intended tro signal to the reader that it is not used in the block calculation (a common convention which is often just the underscore alone).

Sign up to request clarification or add additional context in comments.

2 Comments

old_hash.merge(quantity: old_hash[:quantity] + new_hash[:quantity]) this line has to be executed only when keys are equal, eh? But that checking is not found within your program, but still it works, how? Could you please help me understand?
@Rajagopalan, recall that the block is executed only when the “old” and “new” hashes being merged have same key (g[:lineItemKey]).
1

Input

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

Code

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

Output

[
  { :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 }
]

4 Comments

Rajagopalan looks good! just } is missing at the end, let me try.
You are right, I missed the } while I am copying from my editor. Now I have added.
Looks good! (I remember when you were a kid.) I would say that rather than too few braces you have too many. It would be easier to read with some do ... end's.
@CarySwoveland Thanks! I thought of using it but I wanted to have everything in single line so used the { instead of using do end

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.