Here is your array a
, which I've reduced in size and put the dates in quotes.
arr = [
{:name=>"MR", :data=>{"Sun, 01 Jan 2017"=>44}},
{:name=>"CT", :data=>{"Sun, 01 Jan 2017"=>7}},
{:name=>"US", :data=>{"Sun, 01 Jan 2017"=>1}},
{:name=>"MR", :data=>{"Wed, 01 Feb 2017"=>41}},
{:name=>"CT", :data=>{"Wed, 01 Feb 2017"=>4}},
{:name=>"CT", :data=>{"Wed, 01 Mar 2017"=>6}},
{:name=>"XR", :data=>{"Wed, 01 Mar 2017"=>1}},
{:name=>"US", :data=>{"Sat, 01 Apr 2017=>1}}
]
A simple modification is required to convert the date strings to Date
objects1, but I will not do that as the methods below do not depend on whether the dates are strings or Date
objects.
Here are two ways to obtain the desired return value.
Use Enumerable#group_by
arr.group_by { |h| h[:name] }.
map { |k,v| { name: k, data: v.reduce({}) { |h,g| h.merge(g[:data]) } } }
#=> [{:name=>"MR", :data=>{"Sun, 01 Jan 2017"=>44, "Wed, 01 Feb 2017"=>41}},
# {:name=>"CT", :data=>{"Sun, 01 Jan 2017"=>7, "Wed, 01 Feb 2017"=>4,
# "Wed, 01 Mar 2017"=>6}},
# {:name=>"US", :data=>{"Sun, 01 Jan 2017"=>1, "Sat, 01 Apr 2017"=>1}},
# {:name=>"XR", :data=>{"Wed, 01 Mar 2017"=>1}}]
Note that Enumerable#group_by
produces the following hash.
arr.group_by { |h| h[:name] }
#=> {"MR"=>[{:name=>"MR", :data=>{"Sun, 01 Jan 2017"=>44}},
# {:name=>"MR", :data=>{"Wed, 01 Feb 2017"=>41}}],
# "CT"=>[{:name=>"CT", :data=>{"Sun, 01 Jan 2017"=>7}},
# {:name=>"CT", :data=>{"Wed, 01 Feb 2017"=>4}},
# {:name=>"CT", :data=>{"Wed, 01 Mar 2017"=>6}}],
# "US"=>[{:name=>"US", :data=>{"Sun, 01 Jan 2017"=>1}},
# {:name=>"US", :data=>{"Sun, 01 Jan 2017"=>1}}],
# "XR"=>[{:name=>"XR", :data=>{"Wed, 01 Mar 2017"=>1}}]}
One could alternatively replace
v.reduce({}) { |h,g| h.merge(g[:data]) }
with
v.map { |h| h[:data] }.reduce(&:merge)
Use Hash#update (aka merge!
) and Hash#merge
arr.each_with_object({}) do |g,h|
h.update(g[:name]=>g) { |_,n,o| { name: n[:name], data: n[:data].merge(o[:data]) } }
end.values
#=> [{:name=>"MR", :data=>{"Sun, 01 Jan 2017"=>44, "Wed, 01 Feb 2017"=>41}},
# {:name=>"CT", :data=>{"Sun, 01 Jan 2017"=>7, "Wed, 01 Feb 2017"=>4,
# "Wed, 01 Mar 2017"=>6}},
# {:name=>"US", :data=>{"Sun, 01 Jan 2017"=>1, "Sat, 01 Apr 2017"=>1}},
# {:name=>"XR", :data=>{"Wed, 01 Mar 2017"=>1}}]
This uses the form of Hash#update
that employs a block for determining the values of keys that are present in both hashes being merged. See the method's doc for details, particularly the definitions of the three block variables, _
, o
and n
. (The first block variable, which holds the common key, is here represented by an underscore [a valid local variable] to signify that it is not used in the block calculation.)
1 require 'date'; arr = a.map do |h|; key, value = h[:data].flatten; h.merge(data: { Date.parse(key)=>value }); end