0

I want to create a simple data storage using map,

map<int , Person> people;

where Person is

class Person
{
    string name;
    int age;
public:
    Person() : name("noname"), age(0) {}
    Person(string name, int age) : name(name), age(age) {}

    void print()
    {
        cout << "| " << name << " | " << age << endl;
    }
};

and I have a problem with referring to people.name in map::find() function. Probably I just don't know the correct syntax and I can't find the answer. It probably should look something like that, but name is the element of Person class so I guess it should give me an error.

if (people.find(name) != people.end())
    {
        people.erase(name);
        cout << name << " removed from data base" << endl;
    }
else
    {
        cout << name << " not found" << endl;
    }

Sorry for my ignorance, but I started programming quite recently, that's why i don't know basic syntax and get confused sometimes.

Zezwul
  • 3
  • 3
  • Please provide [mcve]. For instance, where are you calling `find` from? Do you get an error? If so, copy-paste it into the question. Do you expect one, but not get it, explain why do you expect it to be an error. – Algirdas Preidžius Feb 27 '18 at 11:28
  • Please have a look at this [C++ books](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) list. – Ron Feb 27 '18 at 11:32
  • 3
    It's possible that `std::map` is not the data structure you're looking for. – molbdnilo Feb 27 '18 at 11:34
  • Did you think what would happen if you have more than one person with the same name ? – Killzone Kid Feb 27 '18 at 12:02
  • @KillzoneKid thanks for pointing that. I wanted to connect the key value with some id number of particular person, in that case _erase_ condition would have to make sure which person is to remove. – Zezwul Feb 27 '18 at 12:17
  • @J.Doe If you do not need frequent lookups by name, you can use `std::find_if` with custom comparator function to search map values. Otherwise you might want to rethink your design to fit your needs better. – Killzone Kid Feb 27 '18 at 12:26
  • @KillzoneKid Actually i think the problem is to access the `name` by a map. Is it executable? – Zezwul Feb 27 '18 at 13:22

2 Answers2

0

Your problem is that you have designed your map wrongly. If you want to use a name to look up a person then the name must be the key of the map, i.e.

map<string, Person>

where the string is the name.

john
  • 85,011
  • 4
  • 57
  • 81
  • I want to use _int_ key value to assign an id to a person. I create person like that: `people[0] = Person("Mike", 40);`, and in some point I want to erase this person with `map::erase` by finding a name. Is it possible to stay with _int_ key type, and still look for a name? – Zezwul Feb 27 '18 at 12:48
  • @Zezwul You could just loop through all the entries in the map until you find the one you want to delete (but note there is nothing in your design to make the name unique). Alternatively you need a more complex data structure. How about a second map from name to ID? Use the name to look up the ID and then the ID to delete from the first map. – john Feb 27 '18 at 14:33
  • I think i am stil missing something out. I thought that ID (key value) could make the name individual, because every person from `people` (`Person` class instance) has its unique _int_ value assigned. – Zezwul Feb 27 '18 at 14:55
  • `people[0] = Person("Mike", 40); people[1] = Person("Mike", 40);`. The IDs are different but the name is the same? Maybe we're talking about different things. – john Feb 27 '18 at 15:01
  • I wanted to connect the key value with some ID number of particular person, in that case _erase_ condition would have to make sure which person is to remove, so when there is a conflict involving two or more same names, ID would be essential to decide which. Like i said, i am a beginner so sometimes i am confused. – Zezwul Feb 27 '18 at 15:18
  • @Zezwul I think we're both saying the same thing. But having made the int ID the key to your map you need to do a bit of extra work to delete from the map using a name. Either of my two suggestions from the previous comment would work. – john Feb 27 '18 at 15:47
  • Yes, I've been trying to iterate through all the entries, but honestly i have no idea how. I tried this `for(map::iterator it = people.begin(); it != people.end(); it++) { if (people.find(name) != people.end()) { cout << "Found " << name << endl; } else { cout << name << " not found" << endl; } }` but with no effect. Of course `name` was defined earlier. – Zezwul Feb 27 '18 at 15:55
  • `for(auto it = people.begin(); it != people.end(); it++) { if (it->second.name == name) { cout << "Found " << name << endl; } else { cout << name << " not found" << endl; } }` `auto` simplifies complex data types (what you wrote wasn't wrong however). When you have an iterator you don't need to use find, the iterator points at the data you want. In this case `it->second` is the `Person`. But you still have a problem because `name` is private in your `Person` class. – john Feb 27 '18 at 16:02
  • Yes, i remembered about privacy. Is there any way to workaround it? – Zezwul Feb 27 '18 at 16:08
  • Just add a getter to your class. – john Feb 27 '18 at 16:29
0

Do you want a std::map<string, Person> instead if you want to have a dictionary of name with the corresponding object?

In that case you can do:

auto person = nameToPersonDictionary.find(personName); 
if (person != nameToPersonDictionary.end()){
    nameToPersonDictionary.erase(personName);
    // person is a reference to object Person 
}else{ // Not found
}

If all you want to do is to build a lookup table you can consider std::set. Another use case if the name is immutable property of the object Person (ie name is never modified for a person) set fits better.

You will have to provide an overload of < operator for set to work.

A simple example to provide the overload would be:

// This assumes that there are can't be two distinct person with same name
bool operator<(const Person &left, const Person &right){
    return left.name < right.name;  
}

Then you can directly query using an instance of Person object.

bashrc
  • 4,725
  • 1
  • 22
  • 49