C++ Sorting Custom Objects in a list - c++

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.

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)));
}

How do you order objects in a priority_queue in C++?

I couldn't find any information on how to order objects in a priority queue. I tried this:
class Person {
...
public:
bool operator<(const Person& p) {
return age < p.age;
}
}
int main() {
priority_queue<Person*> people;
people.push(new Person("YoungMan", 21));
people.push(new Person("Grandma", 83));
people.push(new Person("TimeTraveler", -5000));
people.push(new Person("Infant", 1));
while (!people.empty()) {
cout << people.top()->name;
delete people.top();
people.pop();
}
And it's supposed to give priority based on age (older people get higher priority, and thus leave the queue first), but it doesn't work. But I'm getting this output:
Infant
Grandma
TimeTraveler
YoungMan
And I have no idea what this is ordered by, but it's definitely not age.
priority_queue<Person*> actually orders based on comparing the memory addresses of Person object using the comparator std::less<Person*>.
Declare a priority_queue<Person> instead to order based on the operator< you provided.
Or if you insist on using pointers (for some reason) then declare as:
auto age_comp = [](const std::unique_ptr<Person>& lhs, const std::unique_ptr<Person>& rhs) -> bool {
return *lhs < *rhs;
};
std::priority_queue<std::unique_ptr<Person>, std::vector<std::unique_ptr<Person>>,
decltype(age_comp)> people(age_comp);
// note: must pass age_comp to std::priority_queue constructor here as
// lambda closure types have deleted default constructors
Note that this is using smart pointers not raw pointers, the former are much more commonly used in modern C++ - don't use raw pointers unless you have a very good reason to.
Also, operator< of Person should be const specified as it shouldn't change the Person object it belongs to at any point - the comparator of std::priority_queue expects the const and will likely throw an error if the operator< does not have const specification. So, alter operator< to:
bool operator<(const Person& p) const {
return age < p.age;
}

Sorting vector of instances

I am currently taking a coding class at university and they have VERY specific requirements for the homework assignments.
For this week we have a class called Npt that represents a Nobel Prize winner. This class contains, amongst other things, the Name, the year of the prize and the field of the winner.
Now we should make another class, Nobelpreise, that contains a container for instances of said Nobel prize winner class. We are supposed to sort the elements of that container by the year of the Nobel Prize.
I wasn't able to use the std::sort function with a custom comparator correctly. This is what my code roughly looks like:
class Nobelpreise
{
private:
int numb;
vector<Npt> xx;
public:
Nobelpreise(){numb=0;}
void add(Npt &n1){xx.push_back(n1);numb++;return;}
Npt get_nobel(int i) {return xx[i];}
vector<Npt> get_xx() {return xx;}
int get_numb(){return numb;}
~Nobelpreise(){}
bool mycomp(Npt N1, Npt N2) {return (N1.get_jverl()<N2.get_jverl());}
};
The method get_jverl() comes from the Npt class and just returns the year.
Now the sort function always gives back an error saying that:
sort(Npl.get_xx().begin(), Npl.get_xx().end(), Npl.mycomp)
requires two arguments. Shouldn’t they be provided by the sort function?
I have also tried to overload the < operator which does not seem to work either.
edit1: added end() and removed the () from Npl.mycomp
edit2: we are required to make the comparator function a member of the class
Make your method mycomp static, and write a method which does the sort job.
class Nobelpreise
{
private:
int numb;
vector<Npt> xx;
public:
Nobelpreise(){numb=0;}
~Nobelpreise(){}
vector<Npt>& get_xx() {return xx;}
static bool mycomp( const Npt &N1, const Npt &N2 ) { return N1.get_jverl() < N2.get_jverl(); }
//^^^^^^
void Sort() { std::sort( xx.begin(), xx.end(), &mycomp ); }
};
The method Npt::get_jverl has to be const
returntype get_jverl() const { return ...; }
If you do the sorting outside your class note that you have to return a reference to your vector in your method: vector<Npt>& get_xx() {return xx;}
sort(Npl.get_xx().begin(), Npl.get_xx().end(), &Nobelpreise::mycomp)
Firstly, we'll fix the error that you're fetching two instances of the list:
sort(Npl.get_xx().begin(), Npl.get_xx().end, Npl.mycomp());
Replace with
auto v = Npl.get_xx();
std::sort(v.begin(), v.end(), Npl.mycomp());
I've added () to v.end, too, as we want to call it. However, we don't mean to call Npl::mycomp() here - std::sort wants to receive a function, not the return value:
auto v = Npl.get_xx();
std::sort(v.begin(), v.end(), Npl::mycomp);
This still won't work, as Npl::mycomp is an instance method, and std::sort won't call it with an object pointer for this. As it's implementation doesn't use this, it can be made a static method. Better still, it doesn't use any of the private members, so can be made a free function, outside of any class:
// I've renamed this to say what it does
bool year_precedes(const Npt& a, const Npt& b) {
return a.get_jverl() < b.get_jverl();
}
class Nobelpreise; // make the full declaration available
// for the following to compile
// I've made this a nonmember, too, as it only uses public methods
vector<Npt> sorted_prizes(const Nobelpreise& p)
{
auto v = p.get_xx();
std::sort(v.begin(), v.end(), year_precedes);
return v;
}
That should be enough to help you on your way.
You didn't say which version of C++, but assuming C++11 here's a more modern approach (compared to the two answers that are already here). Your requirements don't mention needing the comparator for anything else, so rather than write a comparator, you can do this:
std::sort(Npl.get_xx().begin(), Npl.get_xx().end(), [](const Npt& lhs, const Npt& rhs) {
return lhs.get_jverl() < rhs.get_jverl()
});
You could put this code in the Sort method of the Nobelpreise class as Rabbid suggests.
Compared to the other approaches here, I'd argue it makes the code more readable as you can see inline what is being compared, instead of having to jump to a new function just to read one more line of code (assuming of course that function is not being used anywhere else). It also makes the code faster because you are passing a lambda rather than a function pointer, but you shouldn't necessarily worry about that at this stage.

Object comparison with Sorting. 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.