C++: Unordered maps and sets for non-primitive types

Since C++ 11, we can use unordered sets and unordered maps. However, these data structures only seem to work with primitive types and strings. If you need a pair or a vector as the key, you’re out of luck. Fortunately, it’s possible to use unordered containers with other types, we just have to do some additional work, namely, define a hash function for the data type you need. Here are a couple of examples:

#include <bits/stdc++.h>
using namespace std;

struct VectorHash {
    template <class T>
    size_t operator()(const vector<T>& v) const {
        // Simple hash function, though not necessarily a good one
        int h = 0;
        for (auto e : v) {
            h ^= hash<T>{}(e);
        }
        return h;
    }
};

struct PairHash {
    template <typename T, typename U>
    auto operator()(const pair<T, U> &p) const -> size_t {
        // Simple hash function, though not necessarily a good one: if p.first and p.second are the
        // same, the final has will be equal to zero.
        return hash<T>{}(p.first) ^ hash<U>{}(p.second);
    }
};

int main() {
    unordered_map<pair<int, int>, int, PairHash> mapOfPairs;
    mapOfPairs[{1, 2}]++;
    mapOfPairs[{1, 2}]++;

    mapOfPairs[{2, 1}]++;
    mapOfPairs[{2, 1}]++;

    for (auto& entry : mapOfPairs) {
        cout << "{" << entry.first.first << ", " << entry.first.second << "}: " << entry.second << "\n";
    }
    cout << "\n";

    unordered_map<vector<int>, int, VectorHash> mapOfVectors;
    mapOfVectors[{1, 2, 3}] = 3;
    mapOfVectors[{1, 2, 3, 4}] = 4;
    mapOfVectors[{1, 2, 3, 4, 5}] = 5;
    for (auto& entry : mapOfVectors) {
        for (auto e : entry.first) {
            cout << e << " ";
        }
        cout << ": ";
        cout << entry.second << "\n";
    }
    return 0;
}

There you have it, an easy way to make unordered containers work with custom types.

References


by

Tags: