Use std::pair as key to std::unordered_map in C++
This post will discuss how to use std::pair as a key to std::unordered_map in C++.
The std::map uses a predicate for key comparison, whose type is specified as the third template parameter of std::map. The default value for this parameter is std::less, which will delegate to operator<. Since operator< is defined for pairs, the following declaration works in C++:
|
1 |
std::map<std::pair<std::string, std::string>, int> m; |
On the other hand, std::unordered_map throws a compilation error when std::pair is used as a key.
|
1 |
std::unordered_map<std::pair<std::string, std::string>, int> m; |
This is because std::unordered_map uses std::hash for computing hash value for its keys and there is no specialization of std::hash for std::pair in the C++ standard library. If we want to use a pair as key to std::unordered_map, we can follow any of the following approaches:
1. Define specialization for std::hash function
Here, the idea is to define our own specialization for std::hash that works with std::pair.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include <iostream> #include <unordered_map> #include <utility> typedef std::pair<std::string, std::string> pair; struct pair_hash { template <class T1, class T2> std::size_t operator() (const std::pair<T1, T2> &pair) const { return std::hash<T1>()(pair.first) ^ std::hash<T2>()(pair.second); } }; int main() { std::unordered_map<pair, int, pair_hash> unordered_map = { {{"C++", "C++11"}, 2011}, {{"C++", "C++14"}, 2014}, {{"C++", "C++17"}, 2017}, {{"Java", "Java 7"}, 2011}, {{"Java", "Java 8"}, 2014}, {{"Java", "Java 9"}, 2017} }; for (auto const &entry: unordered_map) { auto key_pair = entry.first; std::cout << "{" << key_pair.first << "," << key_pair.second << "}, " << entry.second << std::endl; } return 0; } |
Output:
{Java,Java 8}, 2014
{Java,Java 7}, 2011
{Java,Java 9}, 2017
{C++,C++17}, 2017
{C++,C++14}, 2014
{C++,C++11}, 2011
The above code uses XOR as a hash combination function for simplicity. XOR should be avoided as XOR maps identical values and symmetric pairs to 0 (x ^ x == y ^ y == 0, x ^ y == y ^ x). This would end up with far too many collisions in the real world. For production code, we should shift/rotate one of the hashes before XORing.
2. Using boost Library
Another good alternative is to use the boost::hash from Boost.Functional, which can be used to hash integers, floats, pointers, strings, arrays, pairs, and the STL containers.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include <iostream> #include <boost/functional/hash.hpp> #include <unordered_map> #include <utility> typedef std::pair<std::string, std::string> pair; int main() { std::unordered_map<pair, int, boost::hash<pair>> unordered_map = { {{"C++", "C++11"}, 2011}, {{"C++", "C++14"}, 2014}, {{"C++", "C++17"}, 2017}, {{"Java", "Java 7"}, 2011}, {{"Java", "Java 8"}, 2014}, {{"Java", "Java 9"}, 2017} }; for (auto const &entry: unordered_map) { auto key_pair = entry.first; std::cout << "{" << key_pair.first << "," << key_pair.second << "}, " << entry.second << std::endl; } return 0; } |
Output:
{Java,Java 8}, 2014
{Java,Java 9}, 2017
{Java,Java 7}, 2011
{C++,C++17}, 2017
{C++,C++14}, 2014
{C++,C++11}, 2011
That’s all about using std::pair as key to std::unordered_map in C++.
Also See:
Thanks for reading.
To share your code in the comments, please use our online compiler that supports C, C++, Java, Python, JavaScript, C#, PHP, and many more popular programming languages.
Like us? Refer us to your friends and support our growth. Happy coding :)