Galet
Galet

Reputation: 6279

How to remove duplicate row in array of hashes on Ruby on rails

I am using Ruby 2.6 in my application.

I want to remove the duplicate element in array of hashes. Here is my input

array_of_hashes = [
{"Date"=> "2019-05-6", "ID" => 100, "Rate" => 10, "Count" => 1},
{"Date"=> "2019-05-6", "ID" => 100, "Rate" => nil, "Count" => 0},
{"Date"=> "2019-05-6", "ID" => 101, "Rate" => 25, "Count" => 3},
{"Date"=> "2019-05-6", "ID" => 102, "Rate" => nil, "Count" => 0},
{"Date"=> "2019-05-6", "ID" => 102, "Rate" => 35, "Count" => 0},
{"Date"=> "2019-05-6", "ID" => 103, "Rate" => 20, "Count" => 6}
]

I am creating key, value pair from the hash for the need of my application.

result = array_of_hashes.map { |row| [[row['ID'], row['Date'], row] }.to_h

If there are two records with same "ID" and "Date" values in a hash, I want to rows the row where "Rate" != 0 where input records order might shuffle. Here is my Actual and Expected result.

Actual Result:

 {[100, "2019-05-6"]=>{"Date"=>"2019-05-6", "ID"=>100, "Rate"=>nil, "Count"=>0},
 [101, "2019-05-6"]=>{"Date"=>"2019-05-6", "ID"=>101, "Rate"=>25, "Count"=>3},
 [102, "2019-05-6"]=>{"Date"=>"2019-05-6", "ID"=>102, "Rate"=>35, "Count"=>0},
 [103, "2019-05-6"]=>{"Date"=>"2019-05-6", "ID"=>103, "Rate"=>20, "Count"=>6}}

Expected result:

 {[100, "2019-05-6"]=>{"Date"=>"2019-05-6", "ID"=>100, "Rate"=>10, "Count"=>1}, 
 [101, "2019-05-6"]=>{"Date"=>"2019-05-6", "ID"=>101, "Rate"=>25, "Count"=>3},
 [102, "2019-05-6"]=>{"Date"=>"2019-05-6", "ID"=>102, "Rate"=>35, "Count"=>0},
 [103, "2019-05-6"]=>{"Date"=>"2019-05-6", "ID"=>103, "Rate"=>20, "Count"=>6}}

How can I get the above expected result?

Upvotes: 1

Views: 463

Answers (3)

Cary Swoveland
Cary Swoveland

Reputation: 110675

We can construct the desired hash by making a single pass through array_of_hashes.

array_of_hashes.each_with_object({}) do |g,h|
  k = [g['ID'], g['Date']]
  h.update(k=>g) unless h.key?(k) && h[k]['Rate'] != nil
end
  #=> {[100, "2019-05-6"]=>{"Date"=>"2019-05-6", "ID"=>100, "Rate"=>10, "Count"=>1},
  #    [101, "2019-05-6"]=>{"Date"=>"2019-05-6", "ID"=>101, "Rate"=>25, "Count"=>3},
  #    [102, "2019-05-6"]=>{"Date"=>"2019-05-6", "ID"=>102, "Rate"=>35, "Count"=>0},
  #    [103, "2019-05-6"]=>{"Date"=>"2019-05-6", "ID"=>103, "Rate"=>20, "Count"=>6}}

This assumes that if two elements of array_of_hashes match on the values of 'ID' and 'Date', and neither has a value of nil for 'Rate', the first of the two hashes is retained. If the latter of the two should be retained change the second line of the method to:

h.update(k=>g) unless h.key?(k) && g['Rate'].nil?

Upvotes: 2

engineersmnky
engineersmnky

Reputation: 29318

Here is another group by option

array_of_hashes.group_by {|h| h.values_at("ID","Date")}.transform_values do |v|   
  v.find {|r| r["Rate"]}
end

#=> {[100, "2019-05-6"]=>{"Date"=>"2019-05-6", "ID"=>100, "Rate"=>10, "Count"=>1}, 
#    [101, "2019-05-6"]=>{"Date"=>"2019-05-6", "ID"=>101, "Rate"=>25, "Count"=>3}, 
#    [102, "2019-05-6"]=>{"Date"=>"2019-05-6", "ID"=>102, "Rate"=>35, "Count"=>0}, 
#    [103, "2019-05-6"]=>{"Date"=>"2019-05-6", "ID"=>103, "Rate"=>20, "Count"=>6}}

group by id and date then transform the Hash values to the first Hash where "Rate" is not nil.

If multiple values are acceptable then find_all or select could be substituted for find.

If you want the original structure maintained just add values to the end.

Upvotes: 3

Machisuji
Machisuji

Reputation: 738

Use group_by and filter nil rates from the values.

array_of_hashes
  .group_by { |h| [h["ID"], h["Date"]] }
  .map { |key, values| [key, values.reject { |row| row["Rate"].nil? }.last] }
  .to_h

Upvotes: 1

Related Questions