std::set comparator - STL Sort based on multiple attributes

450 Views Asked by At

I'm testing std::set with a custom comparator. But I see the same object getting inserted twice.

Following is the object class:

 class Info
    {
        public:
            Info(string n, string oN, int dom):
                    name(n),
                    objName(oN),
                    domain(dom)
            {}
    

        void setName(std::string n) { name = n;}
        void setObjName(std::string n) { objName = n;}
        void setDomain(int dom) { domain = dom; }

        std::string getName() const { return name;}
        std::string getObjName() const { return objName;}
        int getDomain() const { return domain;}

    private:
        std::string name;
        std::string objName;
        int domain;
};

Following is my custom comparator:

struct InfoCmp {
bool operator() (const Info &lhs, const Info &rhs) const {
    if((lhs.getName() < rhs.getName()) || (lhs.getObjName() < rhs.getObjName()) || (lhs.getDomain() < rhs.getDomain()) ){
        return true;
        }
    return false;
    }
};

Following is the usage:

Info rst1("rst1", "rstObj1", 1);
Info rst2("rst2", "rstObj2", 2);
Info rst3("rst1", "rstObj3", 3);

std::set<Info,InfoCmp> resetSet;

resetSet.insert(rst1);
resetSet.insert(rst2);
resetSet.insert(rst3);
resetSet.insert(rst1);
resetSet.insert(rst2);
resetSet.insert(rst3);

I see rst2 inserted twice, but it shouldn't be as per my comparator.

3

There are 3 best solutions below

0
On BEST ANSWER

I see that you've come up with your own solution, after recognizing from the comments that your original did not impose a strict object ordering as required by set. Here's a different version that only requires operator< and not operator==, making it consistent with the classes and algorithms of the standard library. It also simplifies things if you're e.g. doing a case insensitive comparison.

struct InfoCmp {
    bool operator() (const Info &lhs, const Info &rhs) const {
        if(lhs.getName() < rhs.getName())
            return true;
        if(rhs.getName() < lhs.getName())
            return false;
        if(lhs.getObjName() < rhs.getObjName())
            return true;
        if(rhs.getObjName() < lhs.getObjName())
            return false;
        return lhs.getDomain() < rhs.getDomain();
        }
    };
2
On

as suggested by Cory Kramer, adding strict ordering in comparator fixed the problem.

struct InfoCmp {
    bool operator() (const Info &lhs, const Info &rhs) const {
        if((lhs.getName() < rhs.getName())
           || ( (lhs.getName() == rhs.getName()) && (lhs.getObjName() < rhs.getObjName()) )
           || ( (lhs.getName() == rhs.getName()) && (lhs.getObjName() == rhs.getObjName()) && (lhs.getDomain() < rhs.getDomain()) )){
            return true;
            }
        return false;
        }
    };
0
On
struct InfoCmp2 {
    bool operator() (const Info &lhs, const Info &rhs) const {
        return std::make_tuple(lhs.getName(), lhs.getObjName(), lhs.getDomain()) < std::make_tuple(rhs.getName(), rhs.getObjName(), rhs.getDomain());
    }
};

This operator can written with make_tuple as well, and working fine.