Object comparison with Sorting. C++ - c++

I'm having a hard time trying to understand other people's codes here.
I would really appreciate if someone helps me.
Let's say there is an array of object : vpair_list and this vpair_list has a type of class of vpair. So, it would be like this:
class vpair
{
public:
int vid;
int vlabel;
};
bool operator < (const vpair& x, const vpair& y);
vpair* vpair_list;
vpair_list = new vpair[25];
..
sort(vpair_list, vpair_list+j);
What I know from that is sort() compares each element of array vpair_list and sorts them.
The thing is that I just can't understand how that sorting works since the object vpair has two different properties.
Does the sorting work like comparing each property(vid and vlabel) or....? What I thought was the sorting was supposed to be done by comparing specific field or property (either vid or vlabel here).
But this code hasn't got anything to do with that and seems like it just compares the whole object. Could someone tell me how that works?
Thank you in advance.

The standard approach:
class vpair
{
public:
int vid;
int vlabel;
};
bool operator < (vpair const& x, vpair const& y)
{
return std::tie(x.vid, x.vlabel) < std::tie(y.vid, y.vlabel);
}
Of course, the operator can be a member:
class vpair
{
int vid;
int vlabel;
public:
bool operator < (vpair const& y) const
{
return std::tie(vid, vlabel) < std::tie(y.vid, y.vlabel);
}
};

Sort, by default, compares with the operator<. You can implement this operator for your class like so:
public:
bool operator < (const vpair& other) const
{
return (vid < other.vid); // Uses vid but this can be vlable or something else.
}
If you don't have an overload for the operator< with the class you're using, you can always pass in a comparison function as std::sort's third argument:
bool compare_func(vpair i,vpair j) { return (i.vid < j.vid); }
sort(vpair_list, vpair_list+j, compare_func);

Does the sorting work like comparing each property(vid and vlabel) or....?
It happens exactly how you want it to happen.
By default as people have mentioned, the < operator is used by various sort algorithms to arrange elements in ascending order of that operator. However for classes/structs there is no default way to compare them meaning you the programmer has to code it in.
That is what
bool operator < (const vpair& x, const vpair& y);
is. It is just a declaration to the definition of the function the programmer has provided to compare 2 vpair order. The programmer uses his rules to decide and ultimately returns true or false. This is used to sort.
So you can decide exactly how you want it to sort.
bool operator < (const vpair& x, const vpair& y)
{
if(x.vid != y.vid)
return x.vid<y.vid;
return x.vlabel <y.vlabel;
}
This would sort by ascending order of ID, if they are equal, It then sorts by ascending order of vlabel.

Related

Sort a list of Objects by given list of Names that exist [duplicate]

I am having trouble sorting a list of custom class pointers. The class I need to sort are events. These get assigned a random time and I need to do them in the right order.
#include <list>
Class Event{
public:
float time; // the value which I need to sort them by
int type; // to indicate which event i'm dealing with
Event(float tempTime, int tempType)
{
time = tempTime;
type = tempType;
}
int main(){
std::list<Event*> EventList;
list<Event*>::iterator it;
.........
If you could help me sort this out it would be much appreciated! I've been stuck on this for hours now.
Thanks!
Since the list contains pointers, rather than objects, you'll have to provide a custom comparator to compare the objects they point to. And since you're using a list, you have to use its own sort method: the generic std::sort algorithm only works on random-access sequences.
EventList.sort([](Event * lhs, Event * rhs) {return lhs->time < rhs->time;});
or, if you're stuck in the past and can't use lambdas:
struct CompareEventTime {
bool operator()(Event * lhs, Event * rhs) {return lhs->time < rhs->time;}
};
EventList.sort(CompareEventTime());
If the list contained objects (as it probably should), then it might make sense to provide a comparison operator instead:
bool operator<(Event const & lhs, Event const & rhs) {return lhs.time < rhs.time;}
std::list<Event> EventList;
//...
EventList.sort();
You should to that with std::sort. You can either make a custom comparator function that you pass as third argument to the std::sort function, or you can make a < operator overload for your class and std::sort will work naturally.

Differences between various Custom Comparator functions in C++

I found that there are different ways to define custom compare functions for a user defined object. I would like to know the things that I should take into account before choosing one over another.
If I have student object, I can write a custom compare function in the following ways.
struct Student
{
string name;
uint32_t age;
// Method 1: Using operator <
bool operator<(const Student& ob)
{
return age < ob.age;
}
};
// Method 2: Custom Compare Function
bool compStudent(const Student& a, const Student& b)
{
return a.age < b.age;
}
// Method 3: Using operator ()
struct MyStudComp
{
bool operator() (const Student& a, const Student& b)
{
return a.age < b.age;
}
}obComp;
To sort a vector of students I can use either of the below methods.
vector<Student> studs; // Consider I have this object populated
std::sort(studs.begin(), studs.end()); // Method 1
std::sort(studs.begin(), studs.end(), compStudent); // Method 2
std::sort(studs.begin(), studs.end(), obComp); // Method 3
// Method 4: Using Lambda
sort(studs.begin(), studs.end(),
[](const Student& a, const Student& b) -> bool
{
return a.age < b.age;
});
How are these methods different and how should I decide between these. Thanks in advance.
The performance between the different methods is not very different, however, using < will let you be more flexible, and makes using built-ins much easier. I also think using () is kind of weird.
The bigger issue in your example is that your methods should be using const refs instead of values. I.e. bool operator<(Student ob) could be friend bool operator<(const Student& ls, const Student& rs){...}. Also, see here for some examples of different things to consider when overloading operators.
The performance is not going to be noticably different. But it's convenient (and expected) in many cases to have a operator<, so I'd go for that over the special compare function.
There really is no "right" way per se, but if it makes sense for your object to have custom comparators (i.e. operator< etc.) then it would be wise to simply use those. However you may want to sort your object based on a different field member and so providing a custom lambda based on those field comparisons would make sense in that case.
For example, your Student class currently uses an overloaded operator< to compare student ages, so if you are sorting a container of Students based on age then just use this operator implicitly. However, you may want (at another time) to sort based on the names so in this case you could provide a custom lambda as the most elegant method:
std::vector<Student> vec;
// populate vec
std::sort(vec.begin(), vec.end(), [](auto& lhs, auto& rhs) { return lhs.name < rhs.name; });
where the student names are sorted via lexicographical comparisons.
How are these methods different and how should I decide between these.
They differ in their implicit statements of intent. You should use the form that expresses your intent most succinctly.
Relying on operator< implies to someone reading your code that your objects are implicitly ordered, like numbers or strings. They ought to be things that people would say, "well obviously x comes before y".
If the ordering of the map is more abstract, then an ordering function might be better because it expresses the idea that you are imposing an order on the map which may not be a natural order.
in the example you give, I might choose to express the intent in either a function object called ageIsLess for example. As a reader of code using the map is now fully aware of intent.
For example:
#include <cstdint>
#include <set>
#include <string>
#include <algorithm>
#include <iterator>
struct Student
{
std::string name;
std::uint32_t age;
};
struct ByAscendingAge
{
bool operator() (const Student& a, const Student& b) const
{
return a.age < b.age;
}
};
bool age_is_less(const Student& l, const Student& r)
{
return l.age < r.age;
};
bool name_is_less(const Student& l, const Student& r)
{
return l.name < r.name;
};
int main()
{
// this form expresses the intent that any 2 different maps of this type can have different ordering
using students_by_free_function = std::set<Student, bool (*)(const Student&, const Student&)>;
// ordered by age
students_by_free_function by_age_1(age_is_less);
// ordered by name
students_by_free_function by_name_1(name_is_less);
// above two maps are the same type so we can assign them, which implicitly reorders
by_age_1 = by_name_1;
// this form expresses the intent that the ordering is a PROPERTY OF THIS TYPE OF SET
using students_by_age = std::set<Student, ByAscendingAge>;
// note that we don't need a comparator in the constructor
students_by_age by_age_2;
// by_age_2 = by_age_1; // not allowed because the sets are a different type
// but we can assign iterator ranges of course
std::copy(std::begin(by_age_1),
std::end(by_age_1),
std::inserter(by_age_2,
std::end(by_age_2)));
}

C++ Sort a vector of pointers in which the objects represent matrix coordinates

I read a file in which a matrix is filled with 0, 1 and 2.
When I find a 1 I create a BlueCar, when 2 I create RedCar:
class BlueCar : public Car
{
public:
BlueCar(){};
BlueCar(int x, int y);
void move();
virtual ~BlueCar();
};
class RedCar : public Car
{
public:
RedCar(){};
RedCar(int x, int y);
void move();
virtual ~RedCar();
};
class Car
{
public:
Car();
Car(int x, int y);
virtual ~Car();
virtual void move() = 0;
private:
int x,y;
};
With this objects I fill two vectors:
std::vector<BluCar*> *sparseBlu;
std::vector<RedCar*> *sparseRed;
Considering that I need to move the cars of the matrix, and that Blue ones move downward and Red ones move rightward, I think the best approach is to sort this vectors. In that way I can see quickly if the position next to the car I'm considering is empty.
Since Blue cars move downward I think it's better to sort "sparseBlu" first by column and then by row, instead "sparseRed" first by row and then by column.
How can I achieve this result?
It's better (in terms of performance) to sort the vector immediately when I fill it car by car, right?
Short answer:
std::sort(std::begin(*sparseBlu), std::end(*sparseBlu),
[](const BlueCar* lhs, const BlueCar* rhs) -> bool {
return lhs->get_x() < rhs->get_x() ||
(lhs->get_x() == rhs->get_x() && lhs->get_y() < rhs->get_y());
});
std::sort(std::begin(*sparseRed), std::end(*sparseRed),
[](const RedCar* lhs, const RedCar* rhs) -> bool {
return lhs->get_y() < rhs->get_y() ||
(lhs->get_y() == rhs->get_y() && lhs->get_x() < rhs->get_x());
});
Please reconsider, if using pointers really is what you need here. Without pointers you have less noise.
std::vector<BluCar> sparseBlu;
std::vector<RedCar> sparseRed;
std::sort(std::begin(sparseBlu), std::end(sparseBlu),
[](const BlueCar& lhs, const BlueCar& rhs) -> bool {
return lhs.get_x() < rhs.get_x() ||
(lhs.get_x() == rhs.get_x() && lhs.get_y() < rhs.get_y());
});
std::sort(std::begin(sparseRed), std::end(sparseRed),
[](const RedCar& lhs, const RedCar& rhs) -> bool {
return lhs.get_y() < rhs.get_y() ||
(lhs.get_y() == rhs.get_y() && lhs.get_x() < rhs.get_x());
});
When that kind of ordering is natural in your application you might also consider to overload operator < (). That makes the calls to sort() much more explicit:
std::sort(std::begin(sparseBlu), std::end(sparseBlu), std::less<BlueCar>);
std::sort(std::begin(sparseRed), std::end(sparseRed), std::less<RedCar>);
An almost declarative programming style.
If you decide to stick with pointers for whatever reason, please consider to use std::unique_ptr<> or std::shared_ptr<> instead of raw pointers, to manage the objects lifetime correctly. Remember, that there is no garbage collection in C++.
std::sort has an overloaded version with a comparator - a custom function to compare two items: http://en.cppreference.com/w/cpp/algorithm/sort, so you can specify any comparison.
Also you can consider storing your cars in a sparse matrix (std::vector<std::vector<Car>>) where empty cells are just empty. So you don't need to sort and can just look at corresponding cell if it's empty.

std::find not using my defined == operator

I have a simple class that I am storing in a vector as pointers. I want to use a find on the vector but it is failing to find my object. Upon debugging it doesn't seem to call the == operator I've provided. I can 'see' the object in the debugger so I know its there. The code below even uses a copy of the first item in the list, but still fails. The only way I can make it pass is to use MergeLine* mlt = LineList.begin(), which shows me that it is comparing the objects and not using my equality operator at all.
class MergeLine {
public:
std::string linename;
int StartIndex;
double StartValue;
double FidStart;
int Length;
bool operator < (const MergeLine &ml) const {return FidStart < ml.FidStart;}
bool operator == (const MergeLine &ml) const {
return linename.compare( ml.linename) == 0;}
};
Class OtherClass{
public:
std::vector<MergeLine*>LineList;
std::vector<MergeLine*>::iterator LL_iter;
void DoSomething( std::string linename){
// this is the original version that returned LineList.end()
// MergeLine * mlt
// mlt->linename = linename;
// this version doesn't work either (I thought it would for sure!)
MergeLine *mlt =new MergeLine(*LineList.front());
LL_iter = std::find(LineList.begin(), LineList.end(), mlt);
if (LL_iter == LineList.end()) {
throw(Exception("line not found in LineList : " + mlt->linename));
}
MergeLine * ml = *LL_iter;
}
};
cheers,
Marc
Since your container contains pointers and not objects, the comparison will be between the pointers. The only way the pointers will be equal is when they point to the exact same object. As you've noticed the comparison operator for the objects themselves will never be called.
You can use std::find_if and pass it a comparison object to use.
class MergeLineCompare
{
MergeLine * m_p;
public:
MergeLineCompare(MergeLine * p) : m_p(p)
{
}
bool operator()(MergeLine * p)
{
return *p == *m_p;
}
};
LL_iter = std::find_if(LineList.begin(), LineList.end(), MergeLineCompare(mlt));
I think what you really want is to use std::find_if like this:
struct MergeLineNameCompare
{
std::string seachname;
MergeLineNameComp(const std::string &name) : seachname(name)
{
}
bool operator()(const MergeLine * line)
{
return seachname.compare( line->linename ) == 0;
}
};
LL_iter = std::find_if(LineList.begin(), LineList.end(), MergeLineNameCompare(linename) );
The operator == (no matter wich form) is better saved for real comparison of equality.
Operator overloading can't work with pointers as it is ambiguous.
Bjarne Stroustrup :-
References were introduced primarily to support operator overloading.
C passes every function argument by value, and where passing an object
by value would be inefficient or inappropriate the user can pass a
pointer. This strategy doesn’t work where operator overloading is
used. In that case, notational convenience is essential so that a user
cannot be expected to insert address− of operators if the objects are
large.
So, may be not best but still :-
std::vector<MergeLine>LineList;
std::vector<MergeLine>::iterator LL_iter;

C++ Sorting Custom Objects in a list

I am having trouble sorting a list of custom class pointers. The class I need to sort are events. These get assigned a random time and I need to do them in the right order.
#include <list>
Class Event{
public:
float time; // the value which I need to sort them by
int type; // to indicate which event i'm dealing with
Event(float tempTime, int tempType)
{
time = tempTime;
type = tempType;
}
int main(){
std::list<Event*> EventList;
list<Event*>::iterator it;
.........
If you could help me sort this out it would be much appreciated! I've been stuck on this for hours now.
Thanks!
Since the list contains pointers, rather than objects, you'll have to provide a custom comparator to compare the objects they point to. And since you're using a list, you have to use its own sort method: the generic std::sort algorithm only works on random-access sequences.
EventList.sort([](Event * lhs, Event * rhs) {return lhs->time < rhs->time;});
or, if you're stuck in the past and can't use lambdas:
struct CompareEventTime {
bool operator()(Event * lhs, Event * rhs) {return lhs->time < rhs->time;}
};
EventList.sort(CompareEventTime());
If the list contained objects (as it probably should), then it might make sense to provide a comparison operator instead:
bool operator<(Event const & lhs, Event const & rhs) {return lhs.time < rhs.time;}
std::list<Event> EventList;
//...
EventList.sort();
You should to that with std::sort. You can either make a custom comparator function that you pass as third argument to the std::sort function, or you can make a < operator overload for your class and std::sort will work naturally.