Ruby: Passing down key/value after transforming objects in array

  • A+

Given data:

data = [  {"id":14, "sort":1, "content":"9", foo: "2022"},  {"id":14, "sort":4, "content":"5", foo: "2022"},  {"id":14, "sort":2, "content":"1", foo: "2022"},  {"id":14, "sort":3, "content":"0", foo: "2022"},  {"id":15, "sort":4, "content":"4", foo: "2888"},  {"id":15, "sort":2, "content":"1", foo: "2888"},  {"id":15, "sort":1, "content":"3", foo: "2888"},  {"id":15, "sort":3, "content":"3", foo: "2888"},  {"id":16, "sort":1, "content":"8", foo: "3112"},  {"id":16, "sort":3, "content":"4", foo: "3112"},  {"id":16, "sort":2, "content":"4", foo: "3112"},  {"id":16, "sort":4, "content":"9", foo: "3112"} ] 

Got the contents concatenated by their sort and ids with:

formatted = data.group_by { |d| d[:id]}.transform_values do |value_array|   value_array.sort_by { |b| b[:sort] }              .map     { |c| c[:content] }.join end  puts formatted #=> {14=>"9105", 15=>"3134", 16=>"8449"} 

I know that foo exists inside value_array but wondering how can I include foo to exist inside the formatted variable so I can map through it to get the desired output or if it's possible?

Desired Output:

[  {"id":14, "concated_value":"9105", foo: "2022"},  {"id":15, "concated_value":"3134", foo: "2888"},  {"id":16, "concated_value":"8449", foo: "3112"} ] 


Since :foo is unique to :id. You can do this as follows:

data.group_by {|h| h[:id]}.map do |_,sa| {|h| h.delete(:sort) }.reduce do |m,h|       m.merge(h) {|key,old,new| key == :content ? old + new : old }    end.tap {|h| h[:concated_value] = h.delete(:content) } end   #=> [ # {"id":14, foo: "2022", "concated_value":"9105"}, # {"id":15, foo: "2888", "concated_value":"3134"}, # {"id":16, foo: "3112", "concated_value":"8449"} # ] 
  • First we group by id. group_by {|h| h[:id]}
  • Then we dup the hashes in the groups (so as not to destory the original). map(&:dup)
  • Then we sort by sort and delete it at the same time. .sort_by {|h| h.delete(:sort) }
  • Then we merge the groups together and concatenate the content key only. m.merge(h) {|key,old,new| key == :content ? old + new : old }
  • Then we just change the key for content to concated_value tap {|h| h[:concated_value] = h.delete(:content) }


:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: