I want to use a sorting algorithm to sort my vector of clients, but the problem is I have two different criteria to sort them.
To display them to the screen and to save them into a file I need to sort them by ID, but to use it for some other stuff (like knowing the top ten worst clients) I need to sort them by the sum of money they've spent.
These are the overloading functions of the operator== for the client class, but obviously they can't co-exist. Can someone give me a solution for this?
class Client
{
public:
//...
unsigned int getID() const;
double getSum() const;
//...
private:
unsigned int ID;
//...
double sum;
};
bool operator==(const Client &LHS, const Client &RHS)
{
return (LHS.getID() == RHS.getID());
}
bool operator==(const Client &LHS, const Client &RHS)
{
return (LHS.getSum() == RHS.getSum());
}
One of the std::sort function overloads takes a comparator, use that form and provide it with two independent functions or functors (or lambdas) for each instantiation.
class Client
{
public:
//...
unsigned int getID() const;
double getSum() const;
//...
private:
unsigned int ID;
//...
double sum;
};
bool CompareByID(const Client &LHS, const Client &RHS)
{
return (LHS.getID() < RHS.getID());
}
bool CompareBySum(const Client &LHS, const Client &RHS)
{
return (LHS.getSum() < RHS.getSum());
}
// ...
std::sort(container.begin(), container.end(), CompareByID);
Note the sort requires a comparison that obeys its ordering requirements, usually it uses a less than comparison to order the elements. The exact comparison can be different, but needs to obey the same ordering requirements (for further reading, see the information for the std::sort algorithm, and this on strict weak ordering).
As suggested you could use std:sort algorithm.
You need to create 2 function each for sum and Id comparison and pass them as function pointer for comparison.
bool compare_by_ID(const Client &LHS, const Client &RHS)
{
return (LHS.getID() < RHS.getID());
}
bool compare_by_sum(const Client &LHS, const Client &RHS)
{
return (LHS.getSum() < RHS.getSum());
}
and to use them you need to invoke sort in this way
vector<Client> v;
// assume v contains list of clients.
// for comparison by sum
std:sort(v.begin(),v.end(),compare_by_sum);
// for comparison by Id
std:sort(v.begin(),v.end(),compare_by_ID);
Apart from comparison function you could use more sophisticated approach by using functors or function objects.
Instead of relying on operator< to do your sorting, you can explicitly pass a comparator to std::sort:
std::vector<Client> vec;
...
auto sortByID = [](auto lhs, auto rhs){ return lhs.getID() < rhs.getID(); };
std::sort(vec.begin(), vec.end(), sortByID);
//vec is now sorted by ID
auto sortBySum = [](auto lhs, auto rhs){ return lhs.getSum() < rhs.getSum(); }
std::sort(vec.begin(), vec.end(), sortBySum);
//vec is now sorted by Sum
Related
If a class has overloaded an operator to facilitate sorting its objects by a particular attribute, is there a way to overload the operator again to sort by another attribute?
For example, the class below has overloaded operator< to compare its data member minutes, is there a way to do the same for its data member hours or would I just be better off creating a binary predicate for each sorting criteria? Thanks in advance.
class PhoneCall {
friend ostream& operator<<( ostream&,const PhoneCall& );
private:
int minutes;
int hours;
public:
PhoneCall(int = 0);
bool operator<(const PhoneCall&);
};
ostream& operator<<(ostream& out, const PhoneCall& p) {
out << "Phone call lasted " << p.minutes << " minutes" << endl;
return out;
}
PhoneCall::PhoneCall(int ct) {
minutes = ct;
}
bool PhoneCall::operator<(const PhoneCall& p) {
bool less = (minutes < p.minutes)? true: false;
return less;
}
You can also present extra friend functions that provide alternative sorting methods, and in c++11 you can define them inplace:
class PhoneCall {
friend ostream& operator<<( ostream&,const PhoneCall& );
private:
int minutes;
int hours;
public:
PhoneCall(int = 0);
bool operator<(const PhoneCall&);
friend bool LessTime(const PhoneCall& L, const PhoneCall& R)
{ return L.minutes+L.hours*60 < R.minutes+R.hours*60; }
};
std::vector<PhoneCall> calls;
std::sort(calls.begin(), calls.end(), LessTime);
Also, you can overload the comparator on ordered containers such as set:
std::multiset<PhoneCall, LessTime> timeSet;
The use of an inline friend here, compared to a public static method is of minimal benefit. The real benefit of inline friend is when doing operator overloads, such as operator <<
No, what you want is not possible. Instead, std::sort provides two overloads. One that uses operator< by default and an overload which takes a functor for the comparison:
template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp );
This can be used as such:
std::vector<PhoneCall> calls;
std::sort(calls.begin(), calls.end(), [](const PhoneCall& lhs, const PhoneCall& rhs) {
return lhs.hours < rhs.hours;
});
Notice the lambda function as the third parameter does compare by hours.
I have a class named Customer which is overloading the < operator:
bool Customer::operator<(const Customer &other) {
return this->price < other.price;
}
but when I try to initialize the priority queue I get pages of errors.
Customer c1(10,5,12,30);// last parameter is price
Customer c2(10,5,12,2);
priority_queue<Customer , vector<Customer> , less<Customer> > barQ;
barQ.push(c2);
barQ.push(c1);
cout<<barQ.top().price;
Then, i accidentally found out that when i initialize it in following way:
Customer c1(10,5,12,30);
Customer c2(10,5,12,2);
priority_queue<Customer* , vector<Customer*> , less<Customer*> > barQ;
barQ.push(&c2);
barQ.push(&c1);
cout<<barQ.top()->price;
I got no errors and it works well.
So my question is that what's the difference between Customer & Customer*?
I thought it should work when I declare it with Customer, NOT Customer* , why it works otherwise?
The signature of std::less::operator() is (taken from cppreference):
bool operator()( const T& lhs, const T& rhs ) const; // (until C++14)
constexpr bool operator()( const T& lhs, const T& rhs ) const; //(since C++14)
Note the it takes both parameters as const, thus it can only call a const operator<:
bool Customer::operator<(const Customer &other) const { // <--- add const here
return this->price < other.price;
}
Your second code is not really doing what you want it to do, because it uses the built-in operator< for pointers.
you are missing const in operator overload
bool operator<(const Customer &other) const {
return this->price < other.price;
}
you can also use your own comparator instead of std::less. Here is how we can write it.
template<typename type>
struct mycomp {
bool operator()(const type & first, const type & second) const {
return first.price < second.price;
}
};
struct Customer {
int price;
};
int main(){
Customer c1{3};// last parameter is price
Customer c2{2};
priority_queue<Customer , vector<Customer> , mycomp<Customer> > barQ;
barQ.push(c2);
barQ.push(c1);
cout<<barQ.top().price;
return 0;
}
set::less<> doesn't gives an error with Customer* pointer because pointers comparisons can happen as int and it doesn't look for the custom implementation. which is not the case with Customer.
As boost operator document say,template totally_ordered is composed of template less_than_comparable and tempalte equality_comparable.
It means that if a class inherents from template totally_ordered, operator== must be implemented when using operator== or operator!=.
In my view, if operator< is implemented, operator== can be generated automatically like (!(lhs < rhs) && !(rhs < lhs)).So, is operator== necessary ?
code piece:
#include <boost/operators.hpp>
class Foo : public boost::totally_ordered<Foo>
{
public:
explicit Foo(const int num) : m_nMem(num){}
friend bool operator< (const Foo& lhs, const Foo& rhs)
{
return lhs.m_nMem < rhs.m_nMem;
}
// Is operator== necessary ?
// Is operator== equal to (!(lhs < rhs) && !(rhs < lhs)) ?
//friend bool operator== (const Foo& lhs, const Foo& rhs)
//{
// return lhs.m_nMem == rhs.m_nMem;
//}
private:
int m_nMem;
};
int main()
{
Foo foo_1(1), foo_2(2);
foo_1 == foo_2; // compiler error , no operator==
return 0;
}
A strict weak ordering may rate unequal elements equivalent¹
E.g.:
struct Point {
int x,y;
bool operator<(Point const& other) const { return x < other.x; }
};
Here, Points would be ordered by x, and points having equal x would be equivalent according to your suggested implementation.
However, since y may be different, clearly the points are not guaranteed to be equal.
Only if the comparison is in fact a total ordering, then we can generate the equality operation using the relative comparison operators. I can only suspect the library authors
wanted the users to be very conscious of this implications
realized that using (!(lhs < rhs) && !(rhs < lhs)) might lead to suboptimal performance
¹ https://www.sgi.com/tech/stl/StrictWeakOrdering.html
I am sorry if there is similar questions already present on the website, but am currently failing to understand certain parts of the algorithm.
I have a Struct that contains information on user account information for my game:
struct Account
{
int Position;
string Name;
int Score;
string Date;
int Level;
bool operator < (User SOMETHING, User SOMETHING)
{
return (SOMETHING < SOMETHING);
}
};
vector<Account> User;
User.push_back(Account());
User.push_back(Account());
User.push_back(Account());
User[0].Position=1;
User[1].Position=2;
User[2].Position=3;
sort(User.begin(), User.end(), Account);
I need each struct of my vector to be to be organized, say for instance, in descending/ascending order for the "Position" value that each contains.
I just need help on (1) bool operator function (e.g. parameters and return values), and (2) How do I have it so that I can sort it by multiple variables like the positions, scores & level. (Would I need to have 3 bool operator functions?)
Use std::tie, something like this:
struct Account
{
int Position;
string Name;
int Score;
string Date;
int Level;
};
bool operator < (const Account& lhs, const Account& rhs)
{
return std::tie(lhs.Name,lhs.Score,lhs.Date) < std::tie(rhs.Name,rhs.Score,rhs.Date);
}
will sort according to Name first, if Name is equal then according to Score, when both Name and Score are equal then according to Date.
Sorting is simply done by:
std::sort(User.begin(), User.end());
Which, by default, uses operator< on the contained objects of type Account.
Update: I misunderstood your question. In your case you need separated comparators, e.g.
struct by_name_ascending
{
bool operator()(const Account& lhs, const Account& rhs) const
{
return lhs.Name < rhs.Name;
}
};
struct by_score_descending
{
bool operator()(const Account& lhs, const Account& rhs) const
{
return lhs.Score > rhs.Score;
}
};
and sort the vector with
std::sort(User.begin(), User.end(), by_name_ascending());
With lambdas, you could also use
std::sort(User.begin(), User.end(),
[](const Account& lhs, const Account& rhs){
return lhs.Name < rhs.Name;
}
);
directly, simply switching < and > for ascending/descending. No need for other helpers or operators in the class/struct itself.
For some reason, the custom compare seems to be skipped. Never is the debug string printed and the sorting is off.
Can anyone spot what is wrong here?
bool Communication::operator<(const Communication& second) const
{
qDebug() << "Actually sorting";
return (getName().compare(second.getName()) < 0);
}
class Communication
{
public:
bool operator<(const Communication& second) const;
QString getName() const;
void setName(QString nm);
QString commName;
}
void Communication::addComm(vector<Communication*>c)
{
// This is called for sure
lg=c;
std::sort ( lg.begin(), lg.end());
}
Edit:
Below my new approach.
bool Communication::cmp(const Communication* lhs, const Communication* rhs) const
{
return (lhs->getName().compare(rhs->getName()) < 0);
}
...error: no matching function for call to 'sort(std::vector<Communication*>::iterator, std::vector<Communication*>::iterator, <unresolved overloaded function type>)'
Your vector contains pointers:
vector<Communication*> c
but your comparison is for values. You need to implement a comparison for pointers, but this cannot be operator< because you cannot overload that operator for pointers. It should be a function or functor.
bool cmp(const Communication* lhs, const Communication* rhs)
{
return (lhs->getName().compare(rhs->getName()) < 0);
}
std::sort ( lg.begin(), lg.end(), cmp);
Operators won't be overloaded for operators. If you want to sort a sequence of pointers based on a predicate on the pointees you'll need to use a suitable predicate function, e.g.:
std::sort(lg.begin(), lg.end(),
[](Communication const* c0, Communication const* c1){
return *c0 < *c1;
});
You are sorting a vector of Communication*, but your operator< compares const Communication&.