I have a question about how to find the corresponding element which has at least one data number equal to the key being searched in a std::map with structures.
For example, suppose that I am to find the phone number of a person with his name or id in a phonebook (let's assume there's no name that are used more than one person), I will declare a struct named Person and define a std::map containing Person objects and his/her phone number (in std::string):
struct Person {
std::string name;
std::string id;
};
std::map<Person, std::string> phonebook;
As I searched from the Internet, I have known that the struct needs overloaded == and < operators to work with std::map and implemented them:
bool operator==(const Person &person1, const Person &person2) {
return person1.name == person2.name || person1.id == person2.id;
}
bool operator<(const Person &person1, const Person &person2) {
return person1.id < person2.id;
}
I used logical "or" (||) in the overloaded == operator in order to implement the feature that the phone number can be found with a person's name or id, instead of needing both.
I wrote some code to test the feature:
// Add some entries
phonebook[{"Jack", "001"}] = "12345";
phonebook[{"Mike", "002"}] = "12346";
phonebook[{"Eric", "003"}] = "12347";
// Search by name
std::map<Person, std::string>::iterator iter = phonebook.find({"Jack", ""});
if (iter == phonebook.end())
std::cout << "Cannot find the phone number for Jack" << std::endl;
else
std::cout << "Jack's phone number is " << iter->second << std::endl;
// Search by id
iter = phonebook.find({"", "001"});
if (iter == phonebook.end())
std::cout << "Cannot find the phone number for 001" << std::endl;
else
std::cout << "001's phone number is " << iter->second << std::endl;
However, I found that searching by id works very well, but searching by name cannot work. No matter what name is being searched, the phone number can never be found. The code for test above produced the following output:
Cannot find the phone number for Jack
001's phone number is 12345
while the expected output is
Jack's phone number is 12345
001's phone number is 12345
What's more, if I change the way I implement the overloaded < operator to
bool operator<(const Person &person1, const Person &person2) {
return person1.name < person2.name; // change "id" to "name"
}
then searching by name works well, but searching by id will not work.
So how can I implement the feature (to find one's phone number with his/her name or id) with std::map? Or is it cannot be implemented in such a way?
Thanks in advance!
std::map is defined only in terms of the operator<. Two map entries are considered equal if and only if !(a < b) and !(b < a). Your operator== is not helping here, and all the find() call is doing is finding the person with the ID you specified (or name in the second case) because it only considers operator<.
The bigger picture is that std::map::find makes use of the sortedness of std::map (generally implemented as a red black tree) to avoid having to check each element. This property is not helpful if both name and ID participate in your search, because you can't tell if a matching name or is before or after any given node in the tree - you only know that for the ID (or, in the second case, the name).
Unless you want to implement a very fancy and complex way of combining the name and ID into a single sortable/searchable field (no idea how you would do that), you will have to check each element. std::find_if does that for you:
std::find_if(phonebook.begin(), phonebook.end(), [name, id](const std::pair<Person, std::string>& personAndNumber) {
const Person& person = personAndNumber.first;
return person.name == name || person.id == id;
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With