1

I need to store some objects in a std:set (or any other kind of lookup table) and search those by name.

For example, suppose I have a class like (pseudo code):

class Person
{
    std::string mName;
    int mAge;
    ... //etc
};

I would like to store this on a container and search for objects by name. I cannot insert those on a std::set, because as far I know, I must construct an entire object for searching.

My second though was to use a std::map like std::map, but, I will need to duplicate the name for this and I do not want to duplicate the key.

Is there a way to store this kind of object in a std::set (or any other container) and search by a key (not the object) ?

Thank you

bcsanches
  • 2,362
  • 21
  • 32
  • This probably is a duplicate question, but I don't think the one selected is a good fit. – Mark Ransom Jan 09 '15 at 23:34
  • P.S. In C++14 you'll be able to specify a parameter to `find` which is *not* the exact type of the set item. See http://en.cppreference.com/w/cpp/container/set/find for details. – Mark Ransom Jan 09 '15 at 23:43
  • How do you define the ordering for `Person`? – Praetorian Jan 10 '15 at 00:30
  • @MarkRansom how about C++11? – bcsanches Jan 10 '15 at 04:29
  • @Praetorian not relevant for the question. The main concern is: how to store a object in a set and search it based on a property. Assume that the property is unique for each element. – bcsanches Jan 10 '15 at 04:30
  • As Mark mentioned earlier, there is a very elegant c++14 solution to this problem depending on what the default ordering looks like, but I guess it's not relevant. – Praetorian Jan 10 '15 at 04:43

3 Answers3

1

Please take a look at boost::intrusive containers, in particular boost::intrusive::set.

In exchange for some uglification of the Item class comes the great flexibility. Uglification happens because Item must derive from a certain class or it must declare a special member variable to store tree links. This is all nicely wrapped though.

Concerning the particular requests, boost::intrusive::set allows to

  • reuse mName for the key (not unlike std::set)
  • lookup Person by a string (unlike std::set there is no need to construct a Person to do a lookup)
Nick Zavaritsky
  • 1,429
  • 8
  • 19
0

You can use std::find_if, for example if you have a std::set<Person> people

auto match = std::find_if(people.begin(), people.end(), [](const Person& foo){return foo.mName == "Bob";});
if (match != people.end())
{
    std::cout << "Found Bob!"
}

So you don't need to create a full Person object, just a std::string to match their name, for example.

Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
0

So you can use find_if to achieve this:

struct find_by_name {
    find_by_name(const std::string & name) : name(name) {}
    bool operator()(const Person & person) {
        return person.name == name;
    }
private:
    std::string name;
};

// in your code

std::set<Person>::iterator result = std::find_if(people.begin(), people.end(), 
                                              find_by_name("Ben"));
if(result != people.end()) {
    // we found something
}
else {
    // no match
}

This way you search based only on a generated string (in a struct) and its very adaptable, you can initialize it with any string, you can also use it to search for person in any container that implements an iterator.

Fantastic Mr Fox
  • 32,495
  • 27
  • 95
  • 175