Search code examples
c++c++17stdmap

Aggregate values of same key in std::multimap and store in std::map


I essentially want to group the elements of the multimap by key, and obtain the new data structure as a map.

the multimap:

std::multimap<std::string,std::vector<int>> multiMap;
 
    VecA VecB
ABC  10  30
ABC  10  30
DEF  20  20

the output required:

std::map<std::string,std::vector<int>> map;

   VecA VecB
ABC 20  60
DEF 20  20

The following code works for a std::multimap<std::string,int>

std::vector<std::string> a = {ABC,ABC,DEF,GHI};
std::vector<int> b = {10,20,30,40};
    
std::multimap<std::string,int> multiMap;

for (int i = 0; i < a.size(); i++)
{
  multiMap.insert(std::pair<std::string,int >(a[i], b[i]));
}

std::map<std::string,int> map;

std::for_each
(
multiMap.begin(),
multiMap.end(),
[&map] (auto const & i) 
 {
   map[i.first] += i.second;
 }
);

I am unable to change the code in line 19 of the above block (i.e. in the lambda function) to extend in the case of std::multimap<std::string,std::vector<int>> to std::map<std::string,std::vector<int>>


Solution

  • If you want use Arithmetic operators

    Use valarray

    Demo: https://wandbox.org/permlink/SAnHNKNZ0UufqZsN

    #include <iostream>
    #include <string>
    #include <vector>
    #include <map>
    #include <valarray>
    #include <numeric>
    
    int main()
    {
    
        using key_type = std::valarray<int>;
        key_type b = {10,20,30,40};
        
        auto containerToStr = [](const auto& cont)
        {
           return std::accumulate(std::begin(cont), std::end(cont), std::string{}, [](const auto& str, const auto& val){
               return str + " " + std::to_string(val);
           });
        };
    
        
        std::multimap<std::string,key_type> multiMap;
        std::vector<std::string> a = {"ABC","ABC","DEF","GHI"};
        for (size_t i = 0; i < a.size(); i++)
        {
            multiMap.insert(std::pair<std::string,key_type >(a[i], b));
        }
    
        std::cout << "Before sum" << std::endl;
        for (const auto& p : multiMap)
            std::cout << p.first << " " << containerToStr(p.second) << std::endl;
        std::cout << std::endl;
        
        std::map<std::string,key_type> map;
    
    
        std::for_each( multiMap.begin(), multiMap.end(), [&map] (auto const & i) 
            {
                //  map[i.first] += i.second; // caution  map[i.first] create an empty key_type 
                if ( map.count(i.first) > 0)
                {
                    map.at(i.first) += i.second;
                }
                else
                {
                    map.insert({i.first, i.second});
                }
            }
        );
    
        std::cout << "After sum" << std::endl;
        for (const auto& p : map)
            std::cout << p.first << " " << containerToStr(p.second) << std::endl;
    }
    

    If you want use std::vector

    Define your sum function.

    Demo: https://wandbox.org/permlink/FteFkLfwQh0P4wzp

    #include <iostream>
    #include <string>
    #include <vector>
    #include <map>
    #include <numeric>
    
    int main()
    {
    
        using key_type = std::vector<int>;
        key_type b = {10,20,30,40};
        
        auto containerToStr = [](const auto& cont)
        {
           return std::accumulate(std::begin(cont), std::end(cont), std::string{}, [](const auto& str, const auto& val){
               return str + " " + std::to_string(val);
           });
        };
    
        
        std::multimap<std::string,key_type> multiMap;
        std::vector<std::string> a = {"ABC","ABC","DEF","GHI"};
        for (size_t i = 0; i < a.size(); i++)
        {
            multiMap.insert(std::pair<std::string,key_type >(a[i], b));
        }
    
        std::cout << "Before sum" << std::endl;
        for (const auto& p : multiMap)
            std::cout << p.first << " " << containerToStr(p.second) << std::endl;
        std::cout << std::endl;
        
        std::map<std::string,key_type> map;
    
        // Warning : naive function, to rework
        auto your_sum = [](const auto& cont1, const auto& cont2)
        {
            key_type result;
            const auto smaller = std::min(cont1.size(), cont2.size());
            for ( size_t i = 0 ; i < smaller; i++)
            {
                  result.push_back(cont1[i]+cont2[i]);  
            }
            return result;
        };
    
        std::for_each( multiMap.begin(), multiMap.end(), [&map,&your_sum] (auto const & i) 
            {
                //  map[i.first] += i.second; // caution  map[i.first] create an empty key_type 
                if ( map.count(i.first) > 0)
                {
                    map.at(i.first) = your_sum(i.second, map.at(i.first));
                }
                else
                {
                    map.insert({i.first, i.second});
                }
            }
        );
    
        std::cout << "After sum" << std::endl;
        for (const auto& p : map)
            std::cout << p.first << " " << containerToStr(p.second) << std::endl;
    }