Rails mapping array of hashes onto single hash
You could compose Enumerable#reduce
and Hash#merge
to accomplish what you want.
input = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
input.reduce({}, :merge)
is {"testPARAM2"=>"testVAL2", "testPARAM1"=>"testVAL1"}
Reducing an array sort of like sticking a method call between each element of it.
For example [1, 2, 3].reduce(0, :+)
is like saying 0 + 1 + 2 + 3
and gives 6
.
In our case we do something similar, but with the merge function, which merges two hashes.
[{:a => 1}, {:b => 2}, {:c => 3}].reduce({}, :merge)
is {}.merge({:a => 1}.merge({:b => 2}.merge({:c => 3})))
is {:a => 1, :b => 2, :c => 3}
How to merge two arrays of hashes
uniq
would work if you concatenate the arrays in reverse order:
(b + a).uniq { |h| h[:key] }
#=> [
# {:key=>1, :value=>"bar"},
# {:key=>1000, :value=>"something"},
# {:key=>2, :value=>"baz"}
# ]
It doesn't however preserve the order.
Merge an array of hashes by key-value pair
The more-or-less generic and extendable variant would be:
input.
group_by { |h| h['abc_id'] }.
map do |k, v|
v.reduce do |acc, arr|
# use `+` instead of `|` to save duplicates ⇓⇓⇓
acc.merge(arr) { |_, v1, v2| Array === v1 ? v1 | v2 : v1 }
end
end
#⇒ [{"abc_id"=>"1234", "def_id"=>["33", "44"]},
# {"abc_id"=>"5678", "def_id"=>["11", "22", "55", "66"]}]
How to add and merge values from different array of hashes in ruby on rails
Using two helper methods for a DRY code and using Enumerable#sum, Enumerable#group_by, Hash#merge, Hash#transform_values and also other methods you can find in the documentation.
I'm using also Object#then here.
def sum_price_tax(ary)
ary.first.merge ary.then { |ary| { "Price" => ary.sum { |h| h["Price"].to_f }, "Tax" => ary.sum { |h| h["Tax"].to_f} } }
end
def group_and_sum(array_of_hash)
array_of_hash.group_by { |h| h["Date"] }.transform_values { |ary| sum_price_tax(ary) }
end
After the methods are defined, you can do:
a1 = group_and_sum(array_of_hash1)
a2 = group_and_sum(array_of_hash2)
a1.map { |k, v| v.merge(a2[k] || {}) { |h, old_val, new_val| old_val.is_a?(Float) ? old_val - new_val : old_val } }
#=> [{"Date"=>"2019-07-01", "Country"=>"US", "Email"=>"sample1@gmail.com", "Price"=>2.2121007000000006, "Tax"=>6.254, "Name"=>"John"}, {"Date"=>"2019-06-30", "Country"=>"US", "Email"=>"sample3@gmail.com", "Price"=>17.854323, "Tax"=>7.12343}, {"Date"=>"2019-07-02", "Country"=>"UK", "Email"=>"sample4@gmail.com", "Price"=>4.0019787, "Tax"=>1.9798780000000002, "Name"=>"Sam"}]
In this way also the "Name"
is present.
One way you could get rid of
"Name"
is using Object#tap and Hash#delete:a1.map { |k, v| v.merge(a2[k] || {}) { |h, old_val, new_val| old_val.is_a?(Float) ? old_val - new_val : old_val }.tap { |h| h.delete("Name") } }
#=> [{"Date"=>"2019-07-01", "Country"=>"US", "Email"=>"sample1@gmail.com", "Price"=>2.2121007000000006, "Tax"=>6.254}, {"Date"=>"2019-06-30", "Country"=>"US", "Email"=>"sample3@gmail.com", "Price"=>17.854323, "Tax"=>7.12343}, {"Date"=>"2019-07-02", "Country"=>"UK", "Email"=>"sample4@gmail.com", "Price"=>4.0019787, "Tax"=>1.9798780000000002}]
How to merge arrays of hashes with matching a column value
This answer only works if array2
doesn't contain any duplicate Plotting ID
values. (If there are duplicate Plotting ID
it still works, but it uses the last record of the once precent in the array.)
array1 = [{"Date" => "2019-01-01", "Plot ID" => 234}, {"Date" => "2019-01-01", "Plot ID" => 235}, {"Date" => "2019-01-01", "Plot ID" => 236}, {"Date" => "2019-01-01", "Plot ID" => 237}, {"Date" => "2019-01-01", "Plot ID" => 238}, {"Date" => "2019-01-01", "Plot ID" => 239}, {"Date" => "2019-01-01", "Plot ID" => 240}, {"Date" => "2019-01-01", "Plot ID" => 241}]
array2 = [{"Date" => "2019-01-01", "Plotting ID" => 234, "size"=> 20, "visit" => 10, "price" => 103}, {"Date" => "2019-01-01", "Plotting ID" => 500, "size"=> 40, "visit" => 22, "price" => 233}, {"Date" => "2019-01-01", "Plotting ID" => 236, "size"=> 25, "visit" => 34, "price" => 423}, {"Date" => "2019-01-01", "Plotting ID" => 600, "size"=> 79, "visit" => 55, "price" => 234}]
array2_lookup = array2.map(&:dup).map { |record| [record.delete('Plotting ID'), record] }.to_h
array2_lookup.default = { 'size' => 0, 'visit' => 0, 'price' => 0 }
pp array1.map { |record| array2_lookup[record['Plot ID']].merge(record) }
# [{"Date"=>"2019-01-01", "size"=>20, "visit"=>10, "price"=>103, "Plot ID"=>234},
# {"size"=>0, "visit"=>0, "price"=>0, "Date"=>"2019-01-01", "Plot ID"=>235},
# {"Date"=>"2019-01-01", "size"=>25, "visit"=>34, "price"=>423, "Plot ID"=>236},
# {"size"=>0, "visit"=>0, "price"=>0, "Date"=>"2019-01-01", "Plot ID"=>237},
# {"size"=>0, "visit"=>0, "price"=>0, "Date"=>"2019-01-01", "Plot ID"=>238},
# {"size"=>0, "visit"=>0, "price"=>0, "Date"=>"2019-01-01", "Plot ID"=>239},
# {"size"=>0, "visit"=>0, "price"=>0, "Date"=>"2019-01-01", "Plot ID"=>240},
# {"size"=>0, "visit"=>0, "price"=>0, "Date"=>"2019-01-01", "Plot ID"=>241}]
The above solution first loops over array2
and converts it to a hash, by deleting the key/value pair 'Plotting ID'
from the hash and using the value as key. For this reason I've added the .map(&:dup)
call, preventing the original hashes in array2
from mutating. If the hash mutation is not an issue for you you can simply remove this.
After creating the lookup hash I've added a default that is used when merging the hashes. All that is left to do now is loop through array1
, lookup the record (if any) or use the default and merge that with the current element.
This answer leaves the keys somewhat scrambled, but since a hash is based on key lookup anyway (not key/value order) this shouldn't be a huge issue. If you'd like to have all keys in the same order you can do so by providing all the keys to the default, setting their values to nil
(or any other value since they are overwritten):
array2_lookup.default = { 'Date' => nil, 'size' => 0, 'visit' => 0, 'price' => 0 }
# ... ^ added placeholder for ordering purposes
# [{"Date"=>"2019-01-01", "size"=>20, "visit"=>10, "price"=>103, "Plot ID"=>234},
# {"Date"=>"2019-01-01", "size"=>0, "visit"=>0, "price"=>0, "Plot ID"=>235},
# {"Date"=>"2019-01-01", "size"=>25, "visit"=>34, "price"=>423, "Plot ID"=>236},
# {"Date"=>"2019-01-01", "size"=>0, "visit"=>0, "price"=>0, "Plot ID"=>237},
# {"Date"=>"2019-01-01", "size"=>0, "visit"=>0, "price"=>0, "Plot ID"=>238},
# {"Date"=>"2019-01-01", "size"=>0, "visit"=>0, "price"=>0, "Plot ID"=>239},
# {"Date"=>"2019-01-01", "size"=>0, "visit"=>0, "price"=>0, "Plot ID"=>240},
# {"Date"=>"2019-01-01", "size"=>0, "visit"=>0, "price"=>0, "Plot ID"=>241}]
Ruby 2.7: How to merge a hash of arrays of hashes and eliminate the duplicates based on one key:value
Ruby has many ways to achieve this.
My first instinct is to group them by id
it and pick only first item from the array.
h.values.flatten.group_by{|x| x["id"]}.map{|k,v| v[0]}
Much cleaner approach is to pick the distinct item based on id after flattening the array of hash which is what Cary Swoveland suggested in the comments
h.values.flatten.uniq { |h| h['id'] }
Merge two array of hashes
You can use this way:
first.zip(second).map { |f, s| f.merge(s) }
#=> [{:frontman=>"aaa", :category=>"bbb", :subcategory=>nil, ...}]
How to merge two array of hashes based on hash's value?
result_array = first_array.map do |first_hash|
second_array.each do |second_hash|
if first_hash[:date] == second_hash[:date]
first_hash[:count] = second_hash[:count]
break
end
end
first_hash
end
Related Topics
Gitlab: Invocation of Gitlab-Shell
How to Convert an Octal Number to Decimal in Ruby
Rails 404 Error for Stylesheet or JavaScript Files
Elegant Way to Only Show Records If They Exist in Rails Erb
Get List Timeline by Using Twitter Gem
Can Someone Explain the Following Code to Me
Reverse a String Each Two Characters with Ruby
Find and Replace Specific Hash and It's Values Within Array
What Is the Directory Structure for Adding Sorbet Rbis to a Gem
Hiding an Attribute in an Activerecord Model
Can Nokogiri Use Single Quotes for Attributes on Saving Xml
How to Display a Regexp Output in an Alphabetical List
Set Site/User Fields in Activeresource
Rspec Matcher That Checks Collection to Include Item That Satisfies Lambda