I want to create a hashmap that I can search for both part of the key somehow. Say I have a pair and I want this to be the key. I wouldn't like to search for the exact {string, id} but sometime to search for the int and sometime for the string, is it possible to do this?
Yes it's possible ! However it's not that useful and has a bad design .
The solution is to use a custom compare type which will customize the comparing operation . If you want one behavior from the comparer then you only need a free context compare type which does whatever you want .
But if you want to alter the behavior from outside the map as needed you will use a comparer with a context which outlives the map
struct CompareContext
{
bool id = false;
};
struct Comparer
{
const CompareContext& ctx;
using arg_t = std::pair<std::string, int>;
Comparer(const CompareContext& ctx) : ctx{ ctx } {}
bool operator()(const arg_t& first, const arg_t& second) const
{
if (ctx.id)
{
std::less<int> l;
return l(first.second, second.second);
}
else
{
std::less<std::string> l;
return l(first.first, second.first);
}
}
};
int main()
{
CompareContext ctx;
Comparer comp{ ctx };
using key_t = Comparer::arg_t;
using val_t = int;
using map_t = std::map<key_t, val_t, Comparer>;
map_t mp{ comp };
}
Another example to search using the string or int of key std::pair<std::string, int>
#include <map>
#include <iostream>
int main()
{
using key_t = std::pair<std::string, int>;
auto cmp = [](const key_t & first, const key_t& second)
{
std::less<int> li;
if (li(first.second, second.second))
return true;
std::less<std::string> ls;
return ls(first.first, second.first);
};
using map_t = std::map<key_t, int, decltype(cmp)>;
key_t key1{ "string1", 1 };
key_t key2{ "string2", 2 };
map_t mp{ cmp };
mp.emplace(key1, 1);
mp.emplace(key2, 2);
key_t search_1{ "string1", 5 }; // match using string
key_t search_2{ "string5", 2 }; // match using int
key_t search_3{ "string3", 3 }; // doesn't exist
auto it = mp.find(search_1); // search and if not exist returns an iterator to end
if (it != mp.end())
std::cout << "Found " << it->second << std::endl;
auto val = mp[search_2];
std::cout << "Found " << val << std::endl;
val = mp[search_3]; // since not found a node will be created with key search_3
std::cout << "Created a node with int = " << val << std::endl;
}
Note that std::map
doesn't search using equality lhs == rhs
but compares with less than and greater than since it is sorted and this approach will be faster
On the other hand std::unordered_map
uses a hasher (usually std::hash
) to test for equality since it isn't ordered .