简体   繁体   中英

combine keys in array of hashes

I map results of my query to create an array of hashes grouped by organisation_id like so:

results.map do |i|
  {
    i['organisation_id'] => {
      name: capability.name,
      tags: capability.tag_list,
      organisation_id: i['organisation_id'],
      scores: {i['location_id'] => i['score']}
    }
  }

a capability is defined outside the map.

The result looks like:

[{1=>{:name=>"cap1", :tags=>["tag A"], :scores=>{26=>4}}}, {1=>{:name=>"cap1", :tags=>["tag A"], :scores=>{12=>5}}}, {2 => {...}}...]

For every organisation_id there is a separate entry in the array. I would like to merge these hashes and combine the scores key as so:

[{1=>{:name=>"cap1", :tags=>["tag A"], :scores=>{26=>4, 12=>5}}}, {2=>{...}}... ]

EDIT

To create the results I use the following AR:

Valuation.joins(:membership)
         .where(capability: capability)
         .select("valuations.id, valuations.score, valuations.capability_id, valuations.membership_id, memberships.location_id, memberships.organisation_id")
         .map(&:serializable_hash)

A Valuation model:

class Valuation < ApplicationRecord
  belongs_to :membership
  belongs_to :capability
end

A Membership model:

class Membership < ApplicationRecord
  belongs_to :organisation
  belongs_to :location
  has_many :valuations
end

results snippet:

[{"id"=>1, "score"=>4, "capability_id"=>1, "membership_id"=>1, "location_id"=>26, "organisation_id"=>1}, {"id"=>16, "score"=>3, "capability_id"=>1, "membership_id"=>2, "location_id"=>36, "organisation_id"=>1}, {"id"=>31, "score"=>3, "capability_id"=>1, "membership_id"=>3, "location_id"=>26, "organisation_id"=>2}, {"id"=>46, "score"=>6, "capability_id"=>1, "membership_id"=>4, "location_id"=>16, "organisation_id"=>2}...

I'll assume for each organization: the name, taglist and organization_id remains the same.

your_hash = results.reduce({}) do |h, i|
  org_id = i['organisation_id']
  h[org_id] ||= {
    name: capability.name, 
    tags: capability.taglist, 
    organisation_id: org_id,
    scores: {}
  }
  h[org_id][:scores][i['location_id']] = i['score']
  # If the location scores are not strictly exclusive, you can also just +=
  h
end

I believe this works, but data is needed to test it.

results.each_with_object({}) do |i,h|
  h.update(i['organisation_id'] => {
    name: capability.name,
    tags: capability.tag_list,
    organisation_id: i['organisation_id'],
    scores: {i['location_id'] => i['score']}) { |_,o,n|
      o[:scores].update(n[:score]); o }
  }
end.values

This uses the form of Hash#update (aka merge! ) that uses a block to determine the values of keys that are present in both hashes being merged. Please consult the doc for the contents of each of the block variables _ , o and n .

Assume, that result is your final array of hashes:

result.each_with_object({}) do |e, obj|
  k, v = e.flatten
  if obj[k]
    obj[k][:scores] = obj[k][:scores].merge(v[:scores])
  else
    obj[k] = v
  end
end

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