Reputation: 1085
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 ?
Upvotes: 1
Views: 64
Reputation: 110675
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
Upvotes: 0
Reputation: 121000
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"}]
Upvotes: 2