Search code examples
c++setunordered-mapunordered-set

C++ best way to copy all elements from vector into map / unordered_map


Using C++, if I want to convert a vector into set or unordered_set container, it can be easily done by:

#include <iostream>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>
using namespace std;

int main() {
    vector<int> vec {1, 2, 2, 3, 3, 3, 4, 4, 4, 4};

    // pass
    unordered_set<int> uSet(vec.begin(), vec.end());
    // pass
    set<int> s(vec.begin(), vec.end());
    // fail
    unordered_map<int, size_t> uMap(vec.begin(), vec.end());
    // fail
    map<int, size_t> m(vec.begin(), vec.end());

    return 0;
}

However, the same technique doesn't work for map or unordered_map containers. I'm wondering if there is a better way to store all the elements from a vector into map / unordered_map container other than:

for (int ele : vec) {
    ++uMap[ele];
}

Also, which copy constructor from https://en.cppreference.com/w/cpp/container/unordered_set/unordered_set is called for the code below:

set<int> s(vec.begin(), vec.end()); 

And why a similar copy constructor from https://en.cppreference.com/w/cpp/container/unordered_map/unordered_map is not available?


Solution

  • Let's look at your set construction.

    vector<int> vec {1, 2, 2, 3, 3, 3, 4, 4, 4, 4};
    
    set<int> s(vec.begin(), vec.end());
    

    This succeeds because the value_type of your set is int. That's not just convenient terminology. A std::set defines a member type called value_type, and I am saying that std::set<int>::value_type is int. Since dereferencing vec.begin() gives a value implicitly convertible to int (well, it is int), this construction succeeds.

    Moving over to map, there is again a member type called value_type. This time, though, value_type is not int, hence your proposed construction fails. The value_type of a map is a pair containing the key-value pair. That is, std::map<int, size_t>::value_type is std::pair<const int, size_t>. Since there is no known conversion from int to any flavor of std::pair, your proposed construction fails.

    If you had instead worked from a vector of pairs, your construction could succeed.

    vector<pair<const int, size_t>> vecp { {1, 2}, {2, 3}, {3, 3}, {4, 4}, {4, 4} };
    
    map<int, size_t> m(vecp.begin(), vecp.end());
    

    This results in m[1] == 2, m[2] == 3, m[3] == 3, and m[4] == 4. The extra {4,4} is dropped because this is a map, not a multimap. (It is unspecified whether the first or second {4,4} is dropped, but whichever is dropped is the extra one.)