Michael Caraway
Michael Caraway

Reputation: 1

Group hash values by key and concatenate values

I need to group a hash by keys and concatenate the values. For example, given this hash:

[
  {"name": "FT002", "data": {"2017-11-01": 1392.0}},
  {"name": "FT004", "data": {"2017-11-01": 4091.0}},
  {"name": "FT002", "data": {"2017-12-01": 1279.0}},
  {"name": "FT004", "data": {"2017-12-01": 3249.0}}
]

I want to produce this hash:

[
  {"name": "FT002", "data": {"2017-11-01": 1392.0, "2017-12-01": 1279.0}},
  {"name": "FT004", "data": {"2017-11-01": 4091.0, "2017-12-01": 3249.0}}
]

Any help would be appreciated.

I tried various iterations of inject, group_by, and merge, but can't seem to get the right result.

Upvotes: 0

Views: 139

Answers (3)

Cary Swoveland
Cary Swoveland

Reputation: 110725

data.group_by { |h| h[:name] }.map do |k,arr|
  { name: k, data: arr.each_with_object({}) { |g,h| h.update(g[:data]) } }
end
  #=> [{:name=>"FT002", :data=>{:"2017-11-01"=>1392.0, :"2017-12-01"=>1279.0}},
  #    {:name=>"FT004", :data=>{:"2017-11-01"=>4091.0, :"2017-12-01"=>3249.0}}]

The first step is to use Enumerable#group_by to produce the following hash.

data.group_by { |h| h[:name] }
  #=> {"FT002"=>[
  #               {:name=>"FT002", :data=>{:"2017-11-01"=>1392.0}},
  #               {:name=>"FT002", :data=>{:"2017-12-01"=>1279.0}}
  #             ],
  #    "FT004"=>[
  #               {:name=>"FT004", :data=>{:"2017-11-01"=>4091.0}},
  #               {:name=>"FT004", :data=>{:"2017-12-01"=>3249.0}}
  #             ]
  #   }

The second step is to simply manipulate the keys and values of this hash. See Hash#update (aka merge!).

An alternative to the second step is the following.

data.group_by { |h| h[:name] }.map do |k,arr|
  { name: k, data: arr.map { |g| g[:data].flatten }.to_h }
end

Note that this uses Hash#flatten, not Array#flatten.

Upvotes: 1

user229044
user229044

Reputation: 239382

You can accomplish this in three short one-liners, first producing a hash mapping names to data, and then producing your desired structure:

data = [
  {"name":"FT002","data":{"2017-11-01":1392.0}},
  {"name":"FT004","data":{"2017-11-01":4091.0}},
  {"name":"FT002","data":{"2017-12-01":1279.0}},
  {"name":"FT004","data":{"2017-12-01":3249.0}}
]

hash = Hash.new { |hash,key| hash[key] = {} }

data.each { |name:, data:| hash[name].merge!(data) }

hash = hash.map { |k,v| { name: k, data: v } }

Upvotes: 1

Anthony L
Anthony L

Reputation: 2169

This should generate the the results you're looking for:

data = [
  {"name":"FT002","data":{"2017-11-01":1392.0}},
  {"name":"FT004","data":{"2017-11-01":4091.0}},
  {"name":"FT002","data":{"2017-12-01":1279.0}},
  {"name":"FT004","data":{"2017-12-01":3249.0}}
]

newData = {}

data.each do |x|
  newData[x[:name]] = [] unless newData[x[:name]].present?
  newData[x[:name]].push x[:data]
end

combined = []
newData.each do |index,value|
  dateData = {}
  value.each do |dateStuff|
    dateStuff.each do |dateIndex, dateValue|
      dateData[dateIndex] = dateValue
    end
  end
  values = {"name": index, "data": dateData}
  combined.push values
end

combined

Upvotes: -1

Related Questions