Home > Software engineering >  Remove duplicate items in an array with a calculation on duplicate items
Remove duplicate items in an array with a calculation on duplicate items

Time:03-22

We have an array of Link objects

links
=> 
[#<Link:0x00000001130ba5b8
  id: 197,                                   
  component_id: 191,           
  quantity: 0.3>,
 #<Link:0x0000000113098648
  id: 198,
  component_id: 191,
  quantity: 0.4>,
 #<Link:0x0000000113073fa0
  id: 201,
  component_id: 193,
  quantity: 0.3>,
 #<Link:0x0000000113073e10
  id: 202,
  component_id: 194,
  quantity: 0.3>,
 #<Link:0x00000001130234d8
  id: 209,
  component_id: 194,
  quantity: 0.8>,
 #<Link:0x00000001130385b8
  id: 208,
  component_id: 199,
  quantity: 0.6>]

We need to get an array with unique component_id's and have the quantity added up. I tried

links.uniq { |x| x.component_id }
=>
[#<Link:0x00000001130ba5b8     
  id: 197,
  component_id: 191,
  quantity: 0.3>,
 #<Link:0x0000000113073fa0
  id: 201,
  component_id: 193,
  quantity: 0.3>,
 #<Link:0x0000000113073e10
  id: 202,
  component_id: 194,
  quantity: 0.3>,
 #<Link:0x00000001130385b8
  id: 208,
  component_id: 199,
  quantity: 0.6>]

Which works but I'm not sure how to get quantity added up in the new unique array so we get the following

[#<Link:0x00000001130ba5b8     
  id: 197,
  component_id: 191,
  quantity: 0.7>,
 #<Link:0x0000000113073fa0
  id: 201,
  component_id: 193,
  quantity: 0.3>,
 #<Link:0x0000000113073e10
  id: 202,
  component_id: 194,
  quantity: 1.1>,
 #<Link:0x00000001130385b8
  id: 208,
  component_id: 199,
  quantity: 0.6>]

If someone could point me in the right direction that would be greatly appreciated. The following works but seems very inefficient

unique = links.uniq { |x| x.component_id }
diff   = links - unique
diff.each do |link|
  x = unique.select { |e| e.component_id == link.component_id }
  x.first.quantity  = link.quantity
end
puts unique

CodePudding user response:

Here's my take on this, with group_by and transform_values:

new_links = links.group_by(&:component_id)
                 .transform_values do |values| 
                   dup = values[0].dup
                   dup.quantity = values.sum(&:quantity) 
                   dup 
                 end.values
pp new_links

The replit !

CodePudding user response:

Input

I changed your input a bit to make a meaningful data structure.

links = [{ Link: 0x00000001130ba5b8,
       id: 197,
       component_id: 191,
       quantity: 0.3 },
     { Link: 0x0000000113098648,
       id: 198,
       component_id: 191,
       quantity: 0.4 },
     { Link: 0x0000000113073fa0,
       id: 201,
       component_id: 193,
       quantity: 0.3 },
     { Link: 0x0000000113073e10,
       id: 202,
       component_id: 194,
       quantity: 0.3 },
     { Link: 0x00000001130234d8,
       id: 209,
       component_id: 194,
       quantity: 0.8 },
     { Link: 0x00000001130385b8,
       id: 208,
       component_id: 199,
       quantity: 0.6 }]

Code

result = links.group_by { |x| x[:component_id] }
          .values
          .map do |arr|
  arr.reduce do |h1, h2|
    h1.merge(h2) do |k, v1, v2|
      k.eql?(:quantity) ? v1   v2 : v1
    end
  end
end

p result

Output

[{:Link=>4614497720, :id=>197, :component_id=>191, :quantity=>0.7}, {:Link=>4614209440, :id=>201, :component_id=>193, :quantity=>0.3}, {:Link=>4614209040, :id=>202, :component_id=>194, :quantity=>1.1}, {:Link=>4613965240, :id=>208, :component_id=>199, :quantity=>0.6}]
  • Related