The key exist in the map variable but find cannot find it - c++

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");
}

Related

How can I erase a key value pair in c++ map?

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.

What should getObjByName() return?

I was working on some c++ code like this:
//c++ code
class MovieInfo;
MovieInfo getMovieInfoByName(String movieName)
{
//search the movieInfoList with movieName
if(FOUND)
return movieInfo;
//TODO: **what should i return if the movieInfo can't be found in the list?**
}
The question is what should i return if the movieInfo can't be found in the list?
You have several options:
Define the MovieInfo class such that an "invalid" instance is possible (similarly to how a default-constructed std::thread doesn't represent an actual thread) and return such an instance.
Make it a precondition of getMovieInfoByName() that the name corresponds to a valid movie info, and simply return a random value if it doesn't (as "violating preconditions leads to undefined behaviour").
Throw an exception when the name is not found.
Return something like boost::optional<MovieInfo>.
Give getMovieInfoByName() an extra parameter of type MovieInfo which would be used as the return value in case no match for the name is found.
It all depends on your intended use of the function.
It depends on the context and preconditions that must be met. For example if you are not sure whether the list contains such a movie by the time you call it, then it would be reasonable to do:
bool getMovieInfoByName(const std::string& movieName, MovieInfo& movieInfo)
{
...
if (FOUND) {
movieInfo = ...;
return true;
}
return false;
}
since the caller will most likely have to know whether the movie with such a movie exists or not.
If it shouldn't happen that getMovieInfoByName will not find the movie, i.e. the caller should already know whether the list contains such a movie by other means, then it is perfectly reasonable to throw an exception since it is exceptional state and rather indicates the wrong usage of this method.
There's also a design pattern called Null Object, which is based on constructing an object, state of which can indicate whether it is a valid / initialized object or it is a dummy instance representing NULL.
In this case the caller would most likely still have to check whether appropriate MovieInfo instance has been returned and this class should provide a method such as bool isValid();.

C++ Help on Class Design Exception Handling

I'm currently learning C++ and practicing my Knowledge by implementing an simple AddressBook Application. I started with an Entry class and an AddressBook class which implements a STL Map to access the entries by the last names of the persons. Now I arrived at the following code:
Entry AddressBook::get_by_last_name(string last_name){
if(this->addr_map.count(last_name) != 0){
//What can I do here?
} else {
return addr_map[last_name];
}
In Scripting Languages I would just return something like -1, Error Message(A List in Python) to indicate that the Function failed. I don't want throw an exception, because it's part of the application logic. The Calling Class should be able to react to the request by printing something on the console or opening a Message Box. Now I thought about implementing the Scripting Languae Approach in C++ by introducing some kind of an Invalid State to the Class Entry. But isn't that bad practice in C++? Could it be that my whole class design is just not appropriate? I appreciate any help. Please keep in mind that I'm still learning C++.
Some quick notes about your code:
if(this->addr_map.count(last_name) != 0){
//What can I do here?
You probably wanted it the other way:
if(this->addr_map.count(last_name) == 0){
//handle error
But your real problem lies here:
return addr_map[last_name];
Two things to note here:
The operator[] for map can do 2 things: If the element exists, it returns it; If the element doesn't exist, it creaets a new (key,value) pair with the specified key and value's default constructor. Probably not what you wanted. However, if your if statement from before would have been the right way, then the latter would never happen because we would knowthe key exists before hand.
In calling count() before, you effectively tell map to try and find the element. By calling operator[], you are telling map to find it again. So, you're doing twice the work to retrieve a single value.
A better (faster) way to do this involves iterators, and the find method:
YourMap::iterator it = addr_map.find(last_name); //find the element (once)
if (it == addr_map.end()) //element not found
{
//handle error
}
return *it.second; //return element
Now, back to the problem at hand. What to do if last_name is not found?
As other answers noted:
Simplest solution would be to return a pointer (NULL if not found)
Use boost::optional.
Simply return the YourMap::iterator but it seems that you are trying to "hide" the map from the user of AddressBook so that's probably a bad idea.
throw an exception. But wait, now you'll have to first check that calling this method is 'safe' (or handle the exception when appropriate). This check requires a boolean method like lastNameExists which would have to be called before calling get_by_last_name. Of course then we'er back to square 1. We're performing 2 find operations to retrieve a single value. It's safe, but if you're doing A LOT of calls to get_by_last_name then this is potentially a good place to optimize with a different solution (besides, arguably the exception is not very constructive: What's wrong with searching for something that isn't there, huh?).
Create a dummy member for Entryindicating that is not a real Entry but that is very poor design (unmanageable, counter intuitive, wasteful - you name it).
As you can see, the first 2 solutions are by far preferable.
One dead-simple option is to change the return type to Entry* (or const Entry*) and then return either the address of the Entry if found, or NULL if not.
If you use Boost, you could return a boost::optional<Entry>, in which case your success code would be the same, but on not-found you'd say return boost::none. This is fancier, but does about the same thing as using a pointer return type.
Throwing an exception is definitely the 'correct' C++ thing to do, based on your function return type.
You might want a function like this to help you, though:
bool AddressBook::lastNameExists(const string &last_name)
{
return addr_map.count(last_name) > 0;
}
Note that your current code returns the entry 'by value' so modifying the returned entry won't update the map. Not sure if this is by accident or design...
Other answers have given various approaches, most of them valid. I didn't see this one yet:
You could add a second parameter with a default value:
Entry AddressBook::get_by_last_name(string last_name, const Entry& default_value){
if(this->addr_map.count(last_name) == 0){
return default_value;
} else {
return addr_map[last_name];
}
In this particular instance, there might not be a sensible default value for a non-existing last name, but in many situations there is.
In C++ you have several ways of signalling that an issue happened in your function.
You can return a special value which the calling code will recognize as an invalid value. This can be a NULL pointer if the function should return a pointer, or a negative value if your function returns an index in an array, or, in the case of a custom class (e.g. your Entry class) you can define a special Entry::invalid value or something similar that can be detected by the calling function.
Your calling code could look like
if ( entryInstance->get_by_last_name("foobar") != Entry::invalid)
{
// here goes the code for the case where the name is valid
} else {
// here goes the code for the case where the name is invalid
}
On the other hand you can use the C++ exceptions mechanism and make your function throw an exception. For this youcan create your own exception class (or use one defined in the standard library, deriving from std::exception). Your function will throw the exception and your calling code will have to catch it with a try...catch statement.
try
{
entryInstance->get_by_last_name("foobar")
}
catch (Exception e)
{
// here goes the code for the case where the name is invalid
}
// here goes the code for the case where the name is valid
Apart from the fact that you could have more than one entry per surname.
Eliminate the getter, and you've solved the problem, or at least shifted it elsewhere.
Tell the AddressBook to display people with given surnames. If there aren't any it can do nothing.
AddressBookRenderer renderer;
AddressBook contacts;
contacts.renderSurnames("smith", renderer);
contacts.renderCompletions("sm", renderer);
//etc
You can do what std::map (and the other containers do).
You return an iterator from your search function.
If the search does not find a value that is useful return an iterator to end().
class AddressBook
{
typedef <Your Container Type> Container;
public:
typedef Container::iterator iterator;
iterator get_by_last_name(std::string const& lastName) {return addr_map.find[lastName];}
iterator end() {return addr_map.end();}
};
Your address book is a container like object.
Not finding an item in a search is likely to happen but it does not have enough context to incorporate error handling code (As the address book could be used from lots of places and each place would have different error handling ideas).
So you must move the test for not found state out of your address book.
just like "Python" we return a marker. In C++ this is usually an iterator to end() which the calling code can check and take the appropriate action.
AddressBook& ab = getAddressBookRef();
AddressBook::iterator find = ab.get_by_last_name("cpp_hobbyist");
if (find != ab.end())
{
Entity& person *find; // Here you have a reference to your entity.
// you can now manipulate as you want.
}
else
{
// Display appropriate error message
}

C++ boost unordered_map - determine if key exists in container

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*/ }

map inserting key values into wrong key?

I have been having this problem for awhile with my code, looking for my mistake I can't see it. I have a map, map I am mapping keywords to the values.
My problem is sometimes when inserting keyword = "Blue" the value is inserting into the Key for "Red".
So instead of,
Key: Red, Value: obj1, obj2
Key: Blue, Value: obj3, obj4
I get,
Key: Red, Value: obj1, obj2, obj4 (obj4 should be keyed to Blue)
Key: Blue, Value: obj3
Not sure what it can be since the same version of the code works for other maps that I have, they just don't have as many Keys.
addKeywordsForObject(const Object* const object, int nKeywords, ...)
{
va_list keywords;
char *keyword;
va_start(keywords, nKeywords);
for (int i = 0; i < nKeywords; i++) {
keyword = va_arg(keywords, char*);
if(objectToKeywordMap.find(keyword) == objectToKeywordMap.end()) {
keywordObject = new ObjectSet();
keywordObject->insert(const_cast<Object* const>(object));
objectToKeywordMap.emplace(StringToObjectSetMap::value_type(keyword,keywordObject));
}
else {
keywordObject->insert(const_cast<Object* const>(object));
objectToKeywordMap.emplace(StringToObjectSetMap::value_type(keyword,keywordObject));
}
}
va_end(keywords);
}
I see no declaration for keywordObject, so I shall assume it's a global. The case of adding a new object to a keyword which is already present is dealt with in the else. What is the value of keywordObject in this case? Who sets it?
There are a number of other problems with this code, some mentioned in other answers and comments.
missing return type for addKeywordsForObject
possible usage of char* as a map key which requires special handling (not clear, because you haven't shown the declaration of StringToObjectSetMap)
a function which takes a variable number of parameters by using ellipsis (almost never a good idea in C++)
use of a global variable where a local variable is appropriate
most probably the problem is that you haven't define your sort function for the key type that that your map has. You need to 'tell' the map how to sort the index-type
For example:
struct sortKey{
bool operator()(key_type const& k1, key_type const& k2){
//define your sort criteria here
}
}
std::map<key_type,value_type,sortKey> the_map;
Please comment if you think there is something else that you are missing.