简体   繁体   中英

Ruby - array of hashes group by based on similar hash key

I have an array of hashes who looks like :

[{"PROJECT"=>"awesome_project1","VERSION"=>128, "STATUS"=>"not required"},
 {"PROJECT"=>"awesome_project2", "VERSION"=>32, "STATUS"=>"finished"},
 {"PROJECT"=>"awesome_project1", "VERSION"=>64, "STATUS"=>"scheduled"}]

And I'm trying to merge the hashes based on one of the hask key, knowing that the key to group_by can change. So if I want to group_by "PROJECT" it would looks like :

[{"PROJECT"=>"awesome_project1","VERSION"=>[128,64], "STATUS"=>["not required","scheduled"]},
 {"PROJECT"=>"awesome_project2", "VERSION"=>32, "STATUS"=>"finished"}]

For the moment the closest I am to the result is by using group_by.

Do you have an idea on how to do it ?

You need to group_by and then reduce values merging them.

input.
  group_by { |h| h["PROJECT"] }.
  values.
  map do |v|
    v.reduce({}) do |acc, h|
      acc.merge(h) { |_, v1, v2| v1 == v2 ? v1 : [*v1, *v2] }
    end
  end

#⇒ [{"PROJECT"=>"awesome_project1",
#    "VERSION"=>[128, 64],
#    "STATUS"=>["not required", "scheduled"]},
#   {"PROJECT"=>"awesome_project2",
#    "VERSION"=>32,
#    "STATUS"=>"finished"}]

You can always solve problems of this kind in one of two ways. The first is to use Enumerable#group_by , as @Aleksei (aka mudsie) has done. The other way is to create a hash whose keys are the values over which the aggregation is being done (here the values of "PROJEXT" ), then at the end extract the values of that hash. That's the approach I've taken below.

Code

def doit(arr)
  arr.each_with_object({}) do |g,h|
    h.update(g["PROJECT"]=>(g.merge("STATUS"=>[g["STATUS"]]))) do |_,o1,n1|
      o1.merge(n1) { |k2,o2,n2| k2=="STATUS" ? (o2+n2) : o2 }  
    end
  end.values
end

Example

arr = [
  {"PROJECT"=>"awesome_project1", "VERSION"=>128, "STATUS"=>"not required"},
  {"PROJECT"=>"awesome_project2", "VERSION"=> 32, "STATUS"=>"finished"},
  {"PROJECT"=>"awesome_project1", "VERSION"=> 64, "STATUS"=>"scheduled"}
]

doit arr
  #=> [{"PROJECT"=>"awesome_project1", "VERSION"=>128,
  #       "STATUS"=>["not required", "scheduled"]},
  #    {"PROJECT"=>"awesome_project2", "VERSION"=>32,
  #       "STATUS"=>["finished"]}] 

This is not quite the return value you asked for. In the second hash you wanted "STATUS"=>"finished" , not "STATUS"=>["finished"] . I intentionally made the value an array, however, as it nearly always saves trouble down-the-line by making all values instances of the same class (here arrays). If it must be "STATUS"=>"finished" , let me know and I will edit my answer.

Explanation

...under construction

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM