Reputation: 6279
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
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
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
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