unordered map without hashing - c++

I'd like to use a structure just like std::map but without ordering, I don't need ordering and my key is pretty huge, so it's "less than" comparision takes time.
So, I saw unordered_map but it has a hash template argument, so, how to use unordered_map without hashing? I'll really need to build my own container?
This question applies to std::set too.
EDIT
Some answers have suggested to create my own hash, but I can't do this, I should have specified it here. The key contains floating point data, so hashing it would be a real bad idea. I need to compare (std::equal_to) directly.

Create your own hash, it's easily done by composing the overloads of std::hash on the fields of your key.
The cppreference example (same as previous link) is quite good (even if you do not need the template stuff):
struct S
{
std::string first_name;
std::string last_name;
};
template <class T>
class MyHash;
template<>
class MyHash<S>
{
public:
std::size_t operator()(S const& s) const
{
std::size_t h1 = std::hash<std::string>()(s.first_name);
std::size_t h2 = std::hash<std::string>()(s.last_name);
return h1 ^ (h2 << 1);
}
};
After that you can use it in the std::unorderd_map:
std::unordered_map<S, Value, MyHash<S>> the_map;
By the way std::unordered_set also need a hash.

You need to spetialize hash object for your key before declaring your unordered_map.
namespace std
{
template <>
class hash<Key>
{
public:
size_t operator()(const Key &) const
{
// ... your hash function for Key object ...
}
};
}
std::unordered_map<Key, Value> myMap;
Example, if I want you use as a key pair:
namespace std
{
class hash<pair<string, int>>
{
public:
size_t operator()(const pair<string, int> &s) const
{
size_t h1 = hash<string>()(s.first);
size_t h2 = hash<int>()(s.second);
return h1 ^ (h2 << 1);
}
};
}
unordered_map<pair<string, int>, string> myMap;

Related

Safe way to use string_view as key in unordered map

My type Val contains std::string thekey.
struct Val
{
std::string thekey;
float somedata;
}
I would like put my type in an unordered map, with thekey as key. For memory and conversion avoidance reasons I would like to have std::string_view as key type. Is it possible to have the key created to point to val.thekey, while using unique_ptr ?
std::unique_ptr<Val> valptr = ...;
std::unordered_map<std::string_view,std::unique_ptr<Val>> themap;
themap[std::string_view(valptr->thekey)] = std::move(valptr); // is this ok and safe?
Safe way to use string_view as key in unordered map
In general there isn't one, because the storage underlying the view might change at any time, invalidating your map invariants.
Associative containers generally own a const key precisely to avoid this.
In your specific case it makes much more sense to use std::unordered_set<Val, ValKeyHash, ValKeyEqual> with suitable hash and equality functors.
Edit, these suitable functors are simply
struct ValKeyHash {
std::size_t operator() (Val const &v)
{
return std::hash<std::string>{}(v.thekey);
}
};
struct ValKeyEqual {
bool operator() (Val const& a, Val const& b)
{
return a.thekey == b.thekey;
}
};
Obviously this leaves us with the slightly unhappy requirement of using a temporary Val{key, dummy_data} for lookups, at least until we can use the C++20 transparent/projected version in the other answer.
In c++20, you should do this
namespace utils {
// adl hash function:
template<class T>
auto hash( T const& t )
->decltype( std::hash<T>{}(t) )
{ return std::hash<T>{}(t); }
// Projected hasher:
template<class Proj>
struct ProjHash {
template<class T>
constexpr std::size_t operator()(T const& t)const {
return hash(Proj{}(t));
}
using is_transparent=std::true_type;
};
// Projected equality:
template<class Proj>
struct ProjEquals {
template<class T, class U>
constexpr std::size_t operator()(T const& t, U const& u)const {
return std::equal_to<>{}( Proj{}(t), Proj{}(u) );
}
using is_transparent=std::true_type;
};
}
// A projection from Val to a string view, or a string view
// to a string view:
struct KeyProj {
std::string_view operator()(Val const& val) const { return val.thekey; }
std::string_view operator()(std::string_view sv) const { return sv; }
};
std::unordered_set<Val, ProjHash<KeyProj>, ProjEquals<KeyProj>> theset;
now you can
theset.find("hello")
to find the element of the set whose key is "hello".
A map is fundamentally wrong here, because the features that a map has that the above set does not don't do the right things. Like mymap["hello"], which goes and creates a Val if it isn't found; we now have a dangling string view in the container.
An intrusive map in std is a set with a projection, not a map with a reference into the value as a key.

how to make custom type work as keys in unordered_map

I'm trying to get customized hash table working on custom types.
Referring to unordered_map constructor error (equal_to templated function)
I have:
typedef pair<int, int> tCoord;
struct hashing_func {
unsigned long operator()(const tCoord& key) const {
unsigned long hash = 0;
int h1 = key.first;
int h2 = key.second;
return h1 ^ (h2 << 1);
}
};
struct key_equal_fn {
bool operator()(const tCoord& t1, const tCoord& t2) const {
return t1.first == t2.first && t1.second == t2.second;
}
};
unordered_map<tCoord, int, hashing_func, key_equal_fn> coord2cnt;
unordered_map<tCoord, int, hashing_func, key_equal_fn>::iterator iter;
iter = coord2cnt.find(coord);
This snippet didn't compile and complained about missing function call to find():
error: no matching member function for call to 'find'
Also tried to use it as coord2cnt[coord], but also got errors on missing [] operator.
I'm compiling using g++ 4.2, which is a bit old, but was fine compiling the following (from the above link):
typedef unordered_map<string, string, hashing_func, key_equal_fn> MapType;
MapType::size_type n = 5;
MapType mymap(n, hashing_func(), key_equal_fn());
Also wonder why this type of definition would work, i.e., specifying 5 in the first parameter. This way of definition seems to be missing in the unordered_map API??
Anyone knows what went wrong here?
Thanks!
UPDATE: as pointed out, string is an internal class that has a built-in hash function available. So I rephrased the question to use a customized type here.
An unordered_map for string as key is handled internally. You don't have to define hash functions for string as keys.
If you want to pass a class or structure as a key. Then you have to define a hash function for it to calculate hash index for keys where collisions are minimum. To find the structure or class object using .find, overload bool operator == in the structure or class that you are using.
Example:
#include <iostream>
#include<unordered_map>
int main() {
std::unordered_map<std::string, int> myMap;
myMap.insert({"asd", 1});
myMap.insert({"qwe", 2});
std::string input;
std::cout<<"Enter String: ";
std::cin>>input;
std::unordered_map<std::string, int>::iterator it = myMap.find(input);
if(it != myMap.end())
std::cout<<"Found";
else
std::cout<<"Not Found";
}
your custom type of function "key_equal_fn" is [string, string],
which is not match with "text2cnt"
If you want do it in a fully-specialized template way,how about this:
template <typename T>
struct key_equal_fn {
bool operator()(const T& t1, const T& t2) const {
return t1==t2;
}
};
template<>
struct key_equal_fn<string> {
bool operator()(const string& t1, const string& t2) const {
return !(t1.compare(t2));
}
};
unordered_map<string, string, hashing_func, key_equal_fn<string> > text2cnt;
unordered_map<string, string, hashing_func, key_equal_fn<string> >::iterator iter;

Access the element of an unordered_set with a hash

template<class T>
struct handle{
...
std::size_t index;
...
};
template<class T>
struct foo{
...
struct eq {
bool operator()(const std::shared_ptr<handle<T>> &a,
const std::shared_ptr<handle<T>> &b) const
{
return a->index == b->index;
}
};
struct hash {
std::size_t operator()(const std::shared_ptr<handle<T>> &a) const
{
return a->index;
}
};
std::unordered_set <std::shared_ptr<handle<T>>, hash, eq> handle_set;
...
};
The handle_set is a view into some std::vector<T>. I basically want to check if someone has a reference to an element in that vector like this
std::size_t index_from_vector = 5;
if(handle_set.count(index_from_vector)){
//handle exisits
}
But this doesn't work because an unordered_set needs the key type, so I would have to do it like this
auto sp = std::make_shared<handle<T>>(..,index_from_vector,..);
if(handle_set.count(sp)){
//handle exisits
}
That means I always would have to create a dummy shared_ptr if I want to check if there is a handle to a specific element in a vector.
Is there a way to access an unordered_set with only the hash?
I currently use a unordered_map for this
std::unordered_map<std::size_t, std::shared_ptr<handle<T>>> handle_map;
But updating handle_map is a bit of a pain because I would need to update handle.index and the key. This gets a bit awkward and an unordered_set would greatly simplify this.
Maybe there is even another data structure that would fit better?

Using string* as a key in an unordered_set

I would like to put use a string* as a key in an unordered_list. I do not want the hash the pointer itself but the string it points to.
I understand I need to create a struct like this:
struct myhash{
size_t operator()(const string * str){
return hash(*str);
}
}
and send it as a a hasher to the map template, but i am not sure how.
That's basically it. You then provide it as the third template parameter to the unordered_map type (Which I will assume to be the C++0x one). I would generalize it so it's usable in any situation, rather than just string:
struct dereference_hash
{
template <typename T>
std::size_t operator()(const T* pX)
{
return std::hash<T>()(*pX);
}
};
typedef std::unordered_map<std::string*, int, dereference_hash> map_type;

std::map default value

Is there a way to specify the default value std::map's operator[] returns when an key does not exist?
While this does not exactly answer the question, I have circumvented the problem with code like this:
struct IntDefaultedToMinusOne
{
int i = -1;
};
std::map<std::string, IntDefaultedToMinusOne > mymap;
No, there isn't. The simplest solution is to write your own free template function to do this. Something like:
#include <string>
#include <map>
using namespace std;
template <typename K, typename V>
V GetWithDef(const std::map <K,V> & m, const K & key, const V & defval ) {
typename std::map<K,V>::const_iterator it = m.find( key );
if ( it == m.end() ) {
return defval;
}
else {
return it->second;
}
}
int main() {
map <string,int> x;
...
int i = GetWithDef( x, string("foo"), 42 );
}
C++11 Update
Purpose: Account for generic associative containers, as well as optional comparator and allocator parameters.
template <template<class,class,class...> class C, typename K, typename V, typename... Args>
V GetWithDef(const C<K,V,Args...>& m, K const& key, const V & defval)
{
typename C<K,V,Args...>::const_iterator it = m.find( key );
if (it == m.end())
return defval;
return it->second;
}
C++17 provides try_emplace which does exactly this. It takes a key and an argument list for the value constructor and returns a pair: an iterator and a bool.: http://en.cppreference.com/w/cpp/container/map/try_emplace
The C++ standard (23.3.1.2) specifies that the newly inserted value is default constructed, so map itself doesn't provide a way of doing it. Your choices are:
Give the value type a default constructor that initialises it to the value you want, or
Wrap the map in your own class that provides a default value and implements operator[] to insert that default.
The value is initialized using the default constructor, as the other answers say. However, it is useful to add that in case of simple types (integral types such as int, float, pointer or POD (plan old data) types), the values are zero-initialized (or zeroed by value-initialization (which is effectively the same thing), depending on which version of C++ is used).
Anyway, the bottomline is, that maps with simple types will zero-initialize the new items automatically. So in some cases, there is no need to worry about explicitly specifying the default initial value.
std::map<int, char*> map;
typedef char *P;
char *p = map[123],
*p1 = P(); // map uses the same construct inside, causes zero-initialization
assert(!p && !p1); // both will be 0
See Do the parentheses after the type name make a difference with new? for more details on the matter.
There is no way to specify the default value - it is always value constructed by the default (zero parameter constructor).
In fact operator[] probably does more than you expect as if a value does not exist for the given key in the map it will insert a new one with the value from the default constructor.
template<typename T, T X>
struct Default {
Default () : val(T(X)) {}
Default (T const & val) : val(val) {}
operator T & () { return val; }
operator T const & () const { return val; }
T val;
};
<...>
std::map<KeyType, Default<ValueType, DefaultValue> > mapping;
More General Version, Support C++98/03 and More Containers
Works with generic associative containers, the only template parameter is the container type itself.
Supported containers: std::map, std::multimap, std::unordered_map, std::unordered_multimap, wxHashMap, QMap, QMultiMap, QHash, QMultiHash, etc.
template<typename MAP>
const typename MAP::mapped_type& get_with_default(const MAP& m,
const typename MAP::key_type& key,
const typename MAP::mapped_type& defval)
{
typename MAP::const_iterator it = m.find(key);
if (it == m.end())
return defval;
return it->second;
}
Usage:
std::map<int, std::string> t;
t[1] = "one";
string s = get_with_default(t, 2, "unknown");
Here is a similar implementation by using a wrapper class, which is more similar to the method get() of dict type in Python: https://github.com/hltj/wxMEdit/blob/master/src/xm/xm_utils.hpp
template<typename MAP>
struct map_wrapper
{
typedef typename MAP::key_type K;
typedef typename MAP::mapped_type V;
typedef typename MAP::const_iterator CIT;
map_wrapper(const MAP& m) :m_map(m) {}
const V& get(const K& key, const V& default_val) const
{
CIT it = m_map.find(key);
if (it == m_map.end())
return default_val;
return it->second;
}
private:
const MAP& m_map;
};
template<typename MAP>
map_wrapper<MAP> wrap_map(const MAP& m)
{
return map_wrapper<MAP>(m);
}
Usage:
std::map<int, std::string> t;
t[1] = "one";
string s = wrap_map(t).get(2, "unknown");
One workaround is to use map::at() instead of [].
If a key does not exist, at throws an exception.
Even nicer, this also works for vectors, and is thus suited for generic programming where you may swap the map with a vector.
Using a custom value for unregistered key may be dangerous since that custom value (like -1) may be processed further down in the code. With exceptions, it's easier to spot bugs.
Expanding on the answer https://stackoverflow.com/a/2333816/272642, this template function uses std::map's key_type and mapped_type typedefs to deduce the type of key and def.
This doesn't work with containers without these typedefs.
template <typename C>
typename C::mapped_type getWithDefault(const C& m, const typename C::key_type& key, const typename C::mapped_type& def) {
typename C::const_iterator it = m.find(key);
if (it == m.end())
return def;
return it->second;
}
This allows you to use
std::map<std::string, int*> m;
int* v = getWithDefault(m, "a", NULL);
without needing to cast the arguments like std::string("a"), (int*) NULL.
Pre-C++17, use std::map::insert(), for newer versions use try_emplace(). It may be counter-intuitive, but these functions effectively have the behaviour of operator[] with custom default values.
Realizing that I'm quite late to this party, but if you're interested in the behaviour of operator[] with custom defaults (that is: find the element with the given key, if it isn't present insert a chosen default value and return a reference to either the newly inserted value or the existing value), there is already a function available to you pre C++17: std::map::insert(). insert will not actually insert if the key already exists, but instead return an iterator to the existing value.
Say, you wanted a map of string-to-int and insert a default value of 42 if the key wasn't present yet:
std::map<std::string, int> answers;
int count_answers( const std::string &question)
{
auto &value = answers.insert( {question, 42}).first->second;
return value++;
}
int main() {
std::cout << count_answers( "Life, the universe and everything") << '\n';
std::cout << count_answers( "Life, the universe and everything") << '\n';
std::cout << count_answers( "Life, the universe and everything") << '\n';
return 0;
}
which should output 42, 43 and 44.
If the cost of constructing the map value is high (if either copying/moving the key or the value type is expensive), this comes at a significant performance penalty, which would be circumvented with C++17's try_emplace().
If you have access to C++17, my solution is as follows:
std::map<std::string, std::optional<int>> myNullables;
std::cout << myNullables["empty-key"].value_or(-1) << std::endl;
This allows you to specify a 'default value' at each use of the map. This may not necessarily be what you want or need, but I'll post it here for the sake of completeness. This solution lends itself well to a functional paradigm, as maps (and dictionaries) are often used with such a style anyway:
Map<String, int> myNullables;
print(myNullables["empty-key"] ?? -1);
Maybe you can give a custom allocator who allocate with a default value you want.
template < class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key,T> > > class map;
With C++20 it is simple to write such getter:
constexpr auto &getOrDefault(const auto &map, const auto &key, const auto &defaultValue)
{
const auto itr = map.find(key);
return itr == map.cend() ? defaultValue : itr->second;
}
Here is a correct approach that will conditionally return a reference if the caller passes in an lvalue reference to the mapped type.
template <typename Map, typename DefVal>
using get_default_return_t = std::conditional_t<std::is_same_v<std::decay_t<DefVal>,
typename Map::mapped_type> && std::is_lvalue_reference_v<DefVal>,
const typename Map::mapped_type&, typename Map::mapped_type>;
template <typename Map, typename Key, typename DefVal>
get_default_return_t<Map, DefVal> get_default(const Map& map, const Key& key, DefVal&& defval)
{
auto i = map.find(key);
return i != map.end() ? i->second : defval;
}
int main()
{
std::map<std::string, std::string> map;
const char cstr[] = "world";
std::string str = "world";
auto& ref = get_default(map, "hello", str);
auto& ref2 = get_default(map, "hello", std::string{"world"}); // fails to compile
auto& ref3 = get_default(map, "hello", cstr); // fails to compile
return 0;
}
If you would like to keep using operator[] just like when you don't have to specify a default value other than what comes out from T() (where T is the value type), you can inherit T and specify a different default value in the constructor:
#include <iostream>
#include <map>
#include <string>
int main() {
class string_with_my_default : public std::string {
public:
string_with_my_default() : std::string("my default") {}
};
std::map<std::string, string_with_my_default> m;
std::cout << m["first-key"] << std::endl;
}
However, if T is a primitive type, try this:
#include <iostream>
#include <map>
#include <string>
template <int default_val>
class int_with_my_default {
private:
int val = default_val;
public:
operator int &() { return val; }
int* operator &() { return &val; }
};
int main() {
std::map<std::string, int_with_my_default<1> > m;
std::cout << m["first-key"] << std::endl;
++ m["second-key"];
std::cout << m["second-key"] << std::endl;
}
See also C++ Class wrapper around fundamental types