Merge two maps, summing values for same keys in C++

荒凉一梦 提交于 2020-01-31 04:45:17

问题


I have two std::map<int,int> maps and wish to merge them into a third map like this: if the same key is found in both maps, create a pair in the third map with the same key and a value which a sum of values from the first and second map, otherwise just copy a pair to the third map. I suspect it can be done with std::accumulate, but I don't understand it well enough.


回答1:


An overly generic solution inspired by std::set_union. Unlike the first suggested answer, this should run in O(n) instead of O(n log n).

Edit: it's still O(n log n) because of insertions into the final map.

#include <map>
#include <iostream>
#include <iterator>
#include <algorithm>

template<class InputIterT1, class InputIterT2, class OutputIterT, class Comparator, class Func>
OutputIterT merge_apply(
    InputIterT1 first1, InputIterT1 last1,
    InputIterT2 first2, InputIterT2 last2,
    OutputIterT result, Comparator comp, Func func) {
  while (true)
  {
    if (first1 == last1) return std::copy(first2, last2, result);
    if (first2 == last2) return std::copy(first1, last1, result);

    if (comp(*first1, *first2) < 0) {
      *result = *first1;
      ++first1;
    } else if (comp(*first1, *first2) > 0) {
      *result = *first2;
      ++first2;
    } else { 
      *result = func(*first1, *first2);
      ++first1;
      ++first2; 
    }   
    ++result;
  }
}

template<class T>
int compare_first(T a, T b) {
  return a.first - b.first;
}

template<class T>
T sum_pairs(T a, T b) {
  return std::make_pair(a.first, a.second + b.second);
}

using namespace std;
int main(int argc, char **argv) {
  map<int,int> a,b,c;

  a[1] = 10; 
  a[2] = 11; 

  b[2] = 100;
  b[3] = 101;

  merge_apply(a.begin(), a.end(), b.begin(), b.end(), inserter(c, c.begin()),
      compare_first<pair<int, int> >, sum_pairs<pair<int, int> >); 

  for (auto item : c)                                                                                                       
    cout << item.first << " " << item.second << endl;
}



回答2:


Here is an example how to do the task with using std::accumulate

#include <iostream>
#include <map>
#include <numeric>

int main() 
{
    std::map<int, int> m1 = { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 } }; 
    std::map<int, int> m2 = { { 2, 5 }, { 3, 1 }, { 5, 5 } };

    for ( const auto &p : m1 ) 
    {
        std::cout << "{ " << p.first << ", " << p.second << " } ";
    }

    std::cout << std::endl;

    for ( const auto &p : m2 ) 
    {
        std::cout << "{ " << p.first << ", " << p.second << " } ";
    }

    std::cout << std::endl;

    std::map<int, int> m3 = std::accumulate( m1.begin(), m1.end(), std::map<int, int>(),
        []( std::map<int, int> &m, const std::pair<const int, int> &p )
        {
            return ( m[p.first] +=p.second, m );
        } );

    m3 = std::accumulate( m2.begin(), m2.end(), m3,
        []( std::map<int, int> &m, const std::pair<const int, int> &p )
        {
            return ( m[p.first] +=p.second, m );
        } );

    for ( const auto &p : m3 ) 
    {
        std::cout << "{ " << p.first << ", " << p.second << " } ";
    }

    std::cout << std::endl;

    return 0;
}

The output is

{ 1, 1 } { 2, 2 } { 3, 3 } { 4, 4 } 
{ 2, 5 } { 3, 1 } { 5, 5 } 
{ 1, 1 } { 2, 7 } { 3, 4 } { 4, 4 } { 5, 5 } 

In fact only for the second map there is a need to use std::accumulate. The first map can be simply copied or assigned to m3.

For example

    std::map<int, int> m3 = m1;
    m3 = std::accumulate( m2.begin(), m2.end(), m3,
        []( std::map<int, int> &m, const std::pair<const int, int> &p )
        {
            return ( m[p.first] +=p.second, m );
        } );



回答3:


I don't think it will be easy (if not impossible) to find a suitable std::algorithm that serves the purpose.

The easiest way would be to first make a copy of map1 to map_result.

Then iterate through map2 and see if any key already exists in map_result then add the values, else add the key_value pair to map_result.

std::map<int,int> map_result( map1 );

for (auto it=map2.begin(); it!=map2.end(); ++it) {
  if ( map_result[it->first] )
    map_result[it->first] += it->second;
  else
    map_result[it->first] = it->second;
}


来源:https://stackoverflow.com/questions/20771786/merge-two-maps-summing-values-for-same-keys-in-c

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!