I have a following array of hashes as the input :-
input =[
{"ID"=>"100", "Key"=>"Field A", "Value"=>"123"},
{"ID"=>"100", "Key"=>"Field B", "Value"=>"333"},
{"ID"=>"100", "Key"=>"Field C", "Value"=>"555"},
{"ID"=>"200", "Key"=>"Field A", "Value"=>"789"},
{"ID"=>"200", "Key"=>"Field B", "Value"=>"999"},
{"ID"=>"200", "Key"=>"Field D", "Value"=>"444"}
]
I would like to transform this array of hash as below
output =[
{"ID"=>"100", "Field A"=>"123", "Field B"=>"333", "Field C" => "555", "Field D" => ""},
{"ID"=>"200", "Field A"=>"789", "Field B"=>"999", "Field C" => "", "Field D" => "444"}
]
I can fetch unique ID and keys as below
irb(main):099:0> unique_id = input.map { |p| p["ID"] }.uniq
=> ["100", "200"]
irb(main):100:0> unique_keys = input.map { |p| p["Key"] }.uniq
=> ["Field A", "Field B", "Field C", "Field D"]
However, I am not able to proceed beyond this to create unique array of hashes for each ID containing keys/value pairs as defined on the input hash.
Something like this might do the job:
keys = input.map { |hash| hash['Key'] }.uniq
result = Hash.new { |result, id| result[id] = {} }
input.each { |hash| result[hash['ID']].merge!(hash['Key'] => hash['Value']) }
result.default = nil # optional: remove the default value
result.each do |id, hash|
(keys - hash.keys).each { |key| hash[key] = '' }
hash['ID'] = id
end
result.values
#=> [{"Field A"=>"123", "Field B"=>"333", "Field C"=>"555", "Field D"=>"", "ID"=>"100"},
# {"Field A"=>"789", "Field B"=>"999", "Field D"=>"444", "Field C"=>"", "ID"=>"200"}]
If you're certain values are never falsy you can replace:
(keys - hash.keys).each { |key| hash[key] = '' }
# with
keys.each { |key| hash[key] ||= '' }
I first create a hash result
to save the resulting hashes, I set the value to defaults to a new hash. Then I get the correct hash based upon ID and merge the key-value pairs into the hash. Lastly I add the missing keys to the hashes and set their values to an empty string and add the ID under which the hash is saved to the hash.
note: If your
input
array contains duplicate key-value pairs, the last one will be used. For example, say both{"ID"=>"100", "Key"=>"Field A", "Value"=>"123"}
and{"ID"=>"100", "Key"=>"Field A", "Value"=>"456"}
are present. Then"Field A" => "456"
will be set, since it's the latter of the two.