How to merge array of hashes to get hash of arrays of values
Take your pick:
hs.reduce({}) {|h,pairs| pairs.each {|k,v| (h[k] ||= []) << v}; h}
hs.map(&:to_a).flatten(1).reduce({}) {|h,(k,v)| (h[k] ||= []) << v; h}
I'm strongly against messing with the defaults for hashes, as the other suggestions do, because then checking for a value modifies the hash, which seems very wrong to me.
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}]
Convert array of hashes to single hash with values as keys
This should do what you want
countries.each_with_object({}) { |country, h| h[country[:country].to_sym] = country[:cost] }
=> {:england=>12.34, :scotland=>56.78}
Merge hash of arrays into array of hashes
I suggest writing the code to permit arbitrary numbers of attributes. It's no more difficult than assuming there are two (:name
and :surname
), yet it provides greater flexibility, accommodating, for example, future changes to the number or naming of attributes:
def squish(h)
keys = h.keys.map(&:to_s)
h.values.transpose.map { |a| keys.zip(a).to_h }
end
h = { name: ["John", "Jane", "Chris"],
surname: ["Doe", "Doe", "Smith"],
age: [22, 34, 96]
}
squish(h)
#=> [{"name"=>"John", "surname"=>"Doe", "age"=>22},
# {"name"=>"Jane", "surname"=>"Doe", "age"=>34},
# {"name"=>"Chris", "surname"=>"Smith", "age"=>96}]
The steps for the example above are as follows:
b = h.keys
#=> [:name, :surname, :age]
keys = b.map(&:to_s)
#=> ["name", "surname", "age"]
c = h.values
#=> [["John", "Jane", "Chris"], ["Doe", "Doe", "Smith"], [22, 34, 96]]
d = c.transpose
#=> [["John", "Doe", 22], ["Jane", "Doe", 34], ["Chris", "Smith", 96]]
d.map { |a| keys.zip(a).to_h }
#=> [{"name"=>"John", "surname"=>"Doe", "age"=>22},
# {"name"=>"Jane", "surname"=>"Doe", "age"=>34},
# {"name"=>"Chris", "surname"=>"Smith", "age"=>96}]
In the last step the first value of b
is passed to map
's block and the block variable is assigned its value.
a = d.first
#=> ["John", "Doe", 22]
e = keys.zip(a)
#=> [["name", "John"], ["surname", "Doe"], ["age", 22]]
e.to_h
#=> {"name"=>"John", "surname"=>"Doe", "age"=>22}
The remaining calculations are similar.
How to merge array of hashes based on hash value but not merge values instead override
Maybe you could just call Array#uniq with a block on email key of the concatenation (Array#+) of the two arrays:
(ary1 + ary2).uniq { |h| h[:email] }
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 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
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'] }
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.
how do I merge array of hashes summing by the value?
Let's group by id and map each result to a structure where the pages are the sum of all the pages for that id
array.
group_by { |item| item[:id] }.
map do |id, items|
page_sum = items.sum { |i| i[:pages] }
Hash[:id, id, :book, items.first[:book], :pages, page_sum]
end
Related Topics
Ruby and "You Must Recompile Ruby with Openssl Support or Change the Sources in Your Gemfile"
How to Build a Rubygems Mirror Server
How to Install Ruby 2 on Ubuntu Without Rvm
Rails Hidden Field Undefined Method 'Merge' Error
Forking a Gem for a Rails Project
Is "Extend Self" the Same as "Module_Function"
Parsing Xls and Xlsx (Ms Excel) Files with Ruby
Bundler Cannot Install Any Gems Without Sudo
Macos Mojave 'Ruby/Config.H' File Not Found
Error Installing Nokogiri on Bundle Install But Already Installed
Ruby Regexp Group Matching, Assign Variables on 1 Line
Devise Logged in Root Route Rails 3
How to Install Ruby 1.9.3 in MAC Os X Mountain Lion Without Xcode
How to Find Out Which Gem Has a Specific Dependency
Is There Equivalent for PHP's Print_R in Ruby/Rails
How to Find the Unique Elements in an Array in Ruby
Errno::Econnrefused: Connection Refused - Connect(2) for Action Mailer