简体   繁体   中英

How to find an element in std::map with structures that has at least one data member equal to the key

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

As @Max Langhof pointed out, you can only use find() to search for keys in a map as maps are sorted on key. To search using value (name in your case), you can use simply iterate over all the keys. Though this approach is not efficient as you have to search through each record in the map.

for (auto it = phonebook.begin(); it != phonebook.end(); it++)
{
    if (it->second == "001")
    {
        return it->first;
    }     
}

// If you reach here, then value was not found
std::cout << "Cannot find the phone number for 001" << std::endl;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM