Home > Software engineering >  How to merge two objects and keep count
How to merge two objects and keep count

Time:05-13

I am building a Rails 5.2 app. In this app I am working with statistics.

I generate two objects:

{
    "total_project": {
        "website": 1,
        "google": 1,
        "instagram": 1
    }
}

And this:

{
    "total_leads": {
        "website": 1,
        "google": 2,
        "client_referral": 1
    }
}

I need to merge these two objects into one single objects that increases the count. The desired result is:

{
    "total_both": {
        "website": 2,
        "google": 3,
        "instagram": 1,
        "client_referral": 1
    }
}

I tried this and it technically works, it merges the objects but the count is not updated:

@total_project = array_projects.group_by { |d| d[:entity_type] }.transform_values(&:count).symbolize_keys
        @total_leads = array_leads.group_by { |d| d[:entity_type] }.transform_values(&:count).symbolize_keys
        @total_sources = merged.merge **@total_project, **@total_leads

Please note that the attributes (sources) are dynamic from the database so I cannot hard code anything. The user can add their own sources.

CodePudding user response:

@total_sources = @total_project.merge(@total_leads) do |key, ts_value, tp_value|
  ts_value   tp_value
end

If there can be more than 2 sources, put everything in an array and do.

@total_sources = source_array.reduce do |accumulator, next_source|
  accumulator.merge(next_source) { |key, v1, v2| v1   v2 }
end

CodePudding user response:

You may compute the desired result as follows.

arr = [{ "total_project": { "website": 1, "google": 1, "instagram": 1 } },
       { "total_leads": { "website": 1, "google": 2, "client_referral": 1 } }]
{ "total_both" => arr.flat_map(&:values)
                     .reduce { |h,g| h.merge(g) { |_,o,n| o n } } }
  #=> {"total_both"=>{:website=>2, :google=>3, :instagram=>1, :client_referral=>1}}

Note that

arr.flat_map(&:values)
  #=> [{:website=>1, :google=>1, :instagram=>1},
  #    {:website=>1, :google=>2, :client_referral=>1}]

Had I used Array#map this would have been

arr.map(&:values)
  #=> [[{:website=>1, :google=>1, :instagram=>1}],
  #    [{:website=>1, :google=>2, :client_referral=>1}]]

See Enumerable#flat_map, Enumerable#reduce and the form of Hash#merge that takes a block (here { |_,o,n| o n }) which returns the values of keys that are present in both hashes being merged. See the doc for merge for definitions of the three block variables (here _, o and n). I have named the first block variable (holding the common key) _ to signal to the reader that it is not used in the block calculation (a common Ruby convention).

  • Related