In boost::unordered_map how do I determine if a key exists in it or not?
boost::unordered_map<vector<int>, MyValueType> my_hash_map;
if (my_hash_map[non-existent key] == NULL)
The above gets compiler error "no match for operator '=='..."
Is the problem that I am using a custom value type or something else?
You can use the find method:
if (my_hash_map.find(non-existent key) == my_hash_map.end())
exist() is spelled count() for any associative container:
if (my_hash_map.count(key)) { /*key exist*/ }
if (!my_hash_map.count(key)) { /*key does not exist*/ }
Related
I have a Server class that processes QJsonObject data and handles it according to a key set in the data.
At the moment, I use a big if-then-else statement to decide what to do like this:
const QString action = jsonObject.value(KEY_ACTION).toString();
if (action == SOME_ACTION) {
// do something
} else if (action == SOME_OTHER_ACTION) {
// do something else
}
and so on. Now, meanwhile, I have quite a lot of actions, and for each one, my server has to check all cases until it finds the correct one. I thus wondered if there was a nicer way to do this.
I thought about having the data processing in different functions and having a QHash with the respective function pointer to the respective function for each action like this:
In the constructor:
const QHash<QString, void(Server::*)(const QJsonObject &)> processFunctionsMap {
{ SOME_ACTION, &Server::processSomeAction },
{ SOME_OTHER_ACTION, &Server::processSomeOtherAction }
}
And the respective functions:
void Server::processSomeAction(const QJsonObject &data)
{
...
}
and then to invoke the matching function:
if (! processFunctionsMap.contains(action)) {
// Catch this case
}
(this->*processFunctionsMap.value(action))(jsonObject);
This seems to work, but I'm not a C++ pro, so my question is if this is the correct way to do it.
Your approach is reasonable, but you've changed the no-matches scenario from executing an else block (potentially doing nothing at all) to instant undefined behavior.
You need to separate the hash lookup from the call so you can insert a check for successful lookup in between. With C++ standard collections (std::map which is a red-black tree, std::unordered_map which is a hashtable), that'd be a call to find(key) which returns an iterator... you compare it to map.end() and make very sure not to dereference if they are equal. QHash, or any other non-standard hashtable, will surely provide something similar.
Understanding what QHash does when key not found
I don't have a lot of experience writing C++ and I'm struggling with an issue. The code below is kind of scraped together from snippets. I am writing a class and I want it to have an attribute map of string keys and function values:
std::map< std::string, std::function<bool(std::string)> > selection_filters;
I then want to add pairs as follows:
auto some_func = [] (std::string value) { return value == "some_val"; };
selection_filters["some_key"] = some_func;
//or
selection_filters.insert(std::make_pair("some_key", some_func));
Such that I can:
if ( selection_filters["some_key"]("function param") == true ) {
//etc..
}
This compiles, but throws an error at runtime:
terminating with uncaught exception of type std::__1::bad_function_call: std::exception
I suspect it may have something to do with a discrepancy between std::function<bool(std::string)> in the map definition, and the use of the lambda function [] (std::string value) { ... };
I would very much like to preserve the use of lambda functions and the possibility to access the functions through the subscript operators on the map (map['some_key'](..)) but my knowledge of C++ is not good enough to come up with a solution.
Can someone please point out the error I'm making (and why it is thrown; I want to learn) and provide suggestions for improvement?
See What causes std::bad_function_call?
Missing or empty function. Be sure to check that "some_key" exists in the map before you call the function,
if(selection_filters.find("some_key") != selection_filters.end())
or at least check that the function has a valid target:
if(selection_filters["some_key"])
When you use the [] operator on an std::map, it will insert a default constructed object (or zero) if it is not already in the map. This can (and will) cause lots of invalid entries for keys that you have not explicitly set.
I'm working on a project that needs unique keys and values so I decided to use maps. Everything works expect for the case where someone may want to change the key value. I'm not sure why, but it causes a fragmentation fault. Can I not do this?
void Journal::set_id(int id){ // journal class
if(join.count(id)){ // join is: static map <int,string> join
cout<<"Journal ID is already being used. Try again."<<endl;
}
else {
join.erase (join.find(id));
join.insert(pair<int,string>(id,name));
}
}
Your logic is flawed.
void Journal::set_id(int id){
if(join.count(id)){
cout<<"Journal ID is already being used. Try again."<<endl;
}
else {
// When you hit this block of the code, there is nothing
// in the map corresponding to 'id'.
// join.find(id) returns the same iterator as join.end()
// Calling erase() with that iterator is causing you the
// problem.
// Don't call erase. Just insert the new item.
// join.erase (join.find(id));
join.insert(pair<int,string>(id,name));
}
}
You have just checked to make sure that id is not being used as a key in the map. If it is, you issue an error. So now you know that id is not in the map. If id is not in the map, join.find(id) will return join.end(), so you really didn't need to call find at all. But more importantly, you then call join.erase(join.end()), which is an error.
See documention for std::map::erase() in cppreference:
The iterator pos must be valid and dereferenceable. Thus the end() iterator (which is valid, but is not dereferencable) cannot be used as a value for pos.
Rather than check whether the key is present, and insert it only if not found, you can simplify the code by just inserting the item, and then checking the return value to see if the insertion succeeded (which it won't if that key was already present).
void Journal::set_id(int id){
if (!(join.insert(std::make_pair(id, name)).second))
cout<<"Journal ID is already being used. Try again."<<endl;
}
This should also improve speed, since it only searches the tree once whereas code doing a count then an insert has to search it twice.
I have created a class named as MyClass and define the map as:
map<string,myClass> myClassSample;
I inserted a variable and key:
myClassSample["id"].setString1_1("hi");
Note: setString1_1 is the setter of the class
Then I use this code to see if the key is available:
if (myClassSample.find("id") != myClassSample.end())
{
printf("Problem");
}
Problem shown as out put. So the condition is TRUE!
but the function can return the string!
return myClassSample["id"].getString1();
Note: getString1 is the getter of the class
You seem to have a misunderstanding of how map::find works. It returns the end() iterator when the key you're searching for cannot be found. So your condition to check if the key is present needs to be
if (myClassSample.find("id") == myClassSample.end())
// ^^ ==, not !=
{
printf("Problem");
}
I am new to json parsing with boost using the property tree.
If I have this hash:
foo = {'test1',true}
ptree pt;
bool v = pt.get<bool>("test2");
I need to check a key exists and if not set it to false.
How do I do that gracefully?
Thanks
// bool optional
boost::optional<bool> v = pt.get_optional<bool>("test2");
// any type actually
boost::optional<std::string> v2 = pt.get_optional<std::string>("test3");
if (v) // key exists
bool bool_value = v.get();
else // not exists
v.set(false);
From boost documentation you can try to find the key and if not_found() then you can push a new key.
assoc_iterator not_found() ; Returns the not-found iterator.
Equivalent to end() in a real associative container.
const_assoc_iterator not_found() const; Returns the not-found
iterator. Equivalent to end() in a real associative container.
assoc_iterator find(const key_type & key) ; Find a child with the
given key, or not_found() if there is none. There is no guarantee
about which child is returned if multiple have the same key.
const_assoc_iterator find(const key_type & key) const; Find a child
with the given key, or not_found() if there is none. There is no
guarantee about which child is returned if multiple have the same key.