Comparison with stl sort() - c++

I'm trying to use stl sort() in a class function. I would like to sort an array of structs that look like this:
struct foo{
double num;
std::string s;
};
with a comparison function like this:
bool aGreaterThanb(foo a, foo b){
if (a.num > b.num){
if(a.num == b.num){
if (anotherOutsideComparison(a.s, b.s)){
return true;
}
}
else
return true;
}
else
return false;
}
But I'm not sure how I can format this to get it to compile. How should I format this so I can call sort(fooarray[0], fooarray[end], aGreaterThanb);? (An example would be great)

Write your comparison function as the operator() method of a structure called a functor:
struct aGreaterThanb
{
bool operator() (const foo& a, const foo& b)
{
// return true iff a is strictly less than b according to your ordering
}
};
Then pass an instance of that functor object to std::sort:
std::sort(fooarray.begin(), fooarray.end(), aGreaterThanb());

If you are using an array of foo like this:
foo fooarray[Foos];
...
sort(fooarray, fooarray + Foos, &aGreaterThanb);
The above code would sort your array in reverse order, since sort expects a less-than comparator.
Additionally to avoid copying a lot of foo-objects around just for comparison, declare your comparator to take const foo& instead of foo as arguments.
bool aGreaterThanb(const foo& a, const foo& b) {

You're supposed to pass iterators — a generalized superset of pointers — to the STL sort function:
std::sort(fooarray, fooarray + end, &aGreaterThanb);

It works just as you want already:
#include <algorithm>
int main()
{
foo data[10];
std::sort(&data[0], &data[10], aGreaterThanb);
}
But you have syntax error. You are missing a brace:
return true;
} // <--- Missing this line
else
return false;
For efficiency you should pass by const reference:
bool aGreaterThanb(foo const& a, foo const& b){

Note that in worst case sort function is up to N^2 comparsions.
And stable_sort complexity is between N*logN and N*(LogN^2)

Make it an operator.
struct foo {
double num;
std::string s;
};
bool operator>(const foo& a, const foo& b) {
return (
(a.num > b.num) ||
((a.num == b.num) &&
anotherOutsideComparison(a.s, b.s))
);
}
// note: std::sort expects operator<
bool operator<(const foo& a, const foo& b) {
return b > a;
}
If you really want to sort using operator>, pass std::greater<foo>() as the functor.
std::sort(foos.begin(), foos.end(), std::greater<foo>());

Related

C++: multimap custom comparator for object keys

Can I somehow use my own function for ordering the pairs in multimap? I have three classes CTimeStamp, CMail and CMailLog. And the thing is in the CMailLog I have
multimap<CTimeStamp, CMail> which I use because for this task I need solution which will be very fast for huge amounts of data and therefor I would need to somehow use method Compare from CTimeStamp when inserting into this multimap. The classes look something like this.
class CTimeStamp {
public:
int compare (const CTimeStamp &x) const;
...
}
class CMail {
...
}
class CMailLog {
public:
...
private:
multimap<CTimeStamp, CMail> logs;
}
I'm not sure how to do this or if it's even possible.
I would need to somehow use method Compare from CTimeStamp when inserting into this multimap
As from the std::multimap documentation, all you need is to either
provide a specialisation for std::less<CTimeStamp>
namespace std {
bool less<CTimeStamp>(const CTimeStamp& a, const CTimeStamp& b) {
return a.compare(b) < 0;
}
}
or
provide a custom comparator at the constructor:
CMailLog() :
logs([](const CTimeStamp& a, const CTimeStamp& b) { return a.compare(b) < 0; })
{}
I used a lambda expression in my last example for the constructor as I consider that's the shortest and most comprehensible form.
In fact any callable with the signature bool (const CTimeStamp&,const CTimeStamp&) would fit well.
You might also write a simple global function
bool foo(const CTimeStamp& a,const CTimeStamp& b) {
return a.compare(b) < 0;
}
or appropriate callable type
struct foo {
bool operator()(const CTimeStamp& a,const CTimeStamp& b) {
return a.compare(b) < 0;
}
};
and pass that one at the
multimap<CTimeStamp, CMail> logs;
in the constructor initializer list:
CMailLog() : logs(foo) {}
Callable struct version
CMailLog() : logs(foo()) {}

Sorting a list by a struct member in c++

I have a list filled with this struct:
struct singlePaymentStruct
{
std::string payer;
int payment;
double amount;
std::time_t timeRec;
singlePaymentStruct() {
payer="Empty";
payment=0;
amount=0;
timeRec = time(0);
}
};
I want to be able to sort this list by any of the fields. How exactly do I do this?
I didn't quite understand how sort method works with something more complex than just a list of records...
Solution found:
singlePaymentList.sort( []( const singlePaymentStruct &a, const singlePaymentStruct &b)
{return a.payer > b.payer;}
);
1.overloading operator<
you can do this by overloading the < operator
struct Foo{
int bar;
bool operator<(Foo &x){
return bar < x.bar;
}
};
2.using lambda expressions
(what is lambda expression?)
Foo array[10];
std::sort(array,array + 10,[](Foo const &l, Foo const &r) {
return l.bar < r.bar; });
3.using custom compare functions
If the possible fields to be used for sorting are known prior, it may be easier to read to implement custom compare functions specifically for the sorting.
struct Foo {
int bar;
SpecialType daa; // Assume daa.IsLessThan() available.
static bool lessBar(const Foo& l, const Foo& r) {
return l.bar < r.bar;
}
static bool lessDaa(const Foo& l, const Foo& r) {
return l.daa.IsLessThan(r.daa);
}
};
Foo array1[10]; // To be sorted by Foo::bar
Foo array2[10]; // To be sorted by Foo::daa
std::sort(array1, array1+10, Foo::lessBar);
std::sort(array2, array2+10, Foo::lessDaa);
std::sort accepts a third optional parameter that is a comparator function. This function should behave as < between elements (i.e. return true when the first is "less than" the second.
For example to sort an std::vector of your structures on increasing payment value what you can do is:
std::sort(data.begin(), data.end(),
[](const singlePaymentStruct& a, const singlePaymentStruct& b) {
return a.payment < b.payment;
});
let the array be struct singlePaymentStruct a[N]
sort(a,a+N,cmp);
bool cmp(struct singlePaymentStruct x, struct singlePaymentStruct y)
{
return x.field < y.field ; //or anything you want to do and return boolean
}
How it works under the hood?
Simply put basically it uses some sorting algoritm like quicksort or mergesort.
Why do we specify comparator functor ?
Well we need that comparator functor to decide the ordering of elements.
The basic thing is in any sorting algortihm the basic operation is comparison..and if we can specify that we are basically controlling the sorting operation.
Hope now you get the pieces together. That's why cmp() takes two values which it will compare and based on which order them.

std::unordered_set of pointers

I have the following struct
struct MyClass {
int myInt;
std::map<int, int> myMap;
};
I want to use unordered_set<MyClass*, PointedObjHash, PointedObEq> but I can't find a valid way to declare PointedObEq.
I tried
struct PointedObjHash {
size_t operator() (MyClass* const& c) const {
std::size_t seed = 0;
boost::hash_combine(seed, c->myInt);
boost::hash_combine(seed, c->myMap);
return seed;
}
and I hope it is fine, but I can't find a way to declare PointedObjEq
--- EDIT ---
If declare operator== inside the class debug never breaks, but I think 'cause MyClass == MyClass* never happens...
struct MyClass {
...
...
bool operator==(MyClass* const& c) {
return this->myInt == c->myInt & this->myMap == c->myMap;
}
If declare operator== inside the class debug never breaks, but I think 'cause MyClass == MyClass* never happens...
The unordered_set needs to use operator== (or PointedObjEq) to double-check the results of the hash function. The hash provides approximate equality, the equality function is used to weed out false positives.
If you've tested adding the same value to the set twice, then you've tested the equality function. To be sure, of course, you can have it print something to the console.
Since it's impossible to define an operator== function with two pointer operands, the PointedObjEq class will be necessary. Note that it takes a MyClass const * on both sides. Also, there's no need to use a reference to a pointer.
So,
struct PointedObjEq {
bool operator () ( MyClass const * lhs, MyClass const * rhs ) const {
return lhs->myInt == rhs->myInt
&& lhs->myMap == rhs->myMap;
}
};
This should do:
struct PointedObEq {
bool operator()(MyClass const * lhs, MyClass const * rhs) const {
return lhs->myInt == rhs->myInt && lhs->myMap == rhs->myMap;
}
};
The reason why your solution does not work is because you have effectively written a mechanism to compare a MyClass with a MyClass*, when you actually need something to compare a MyClass* with a MyClass*.
P.S.: My original answer passed the pointers by const&. Thinking about it, that's a strange coding style, so I changed it to pass the pointers by value.
typedef MyClass* PtrMyClass;
struct PointedObjCompare
{ // functor for operator==
bool operator()(const PtrMyClass& lhs, const PtrMyClass& rhs) const
{
// your code goes here
}
};
std::unordered_set < MyClass*, PointedObjHash, PointedObjCompare > myset;

How to create a set with my customized comparison in c++

Could someone explain me what is going on in this example here?
They declare the following:
bool fncomp (int lhs, int rhs) {return lhs<rhs;}
And then use as:
bool(*fn_pt)(int,int) = fncomp;
std::set<int,bool(*)(int,int)> sixth (fn_pt)
While the example for the sort method in algorithm library here
can do like this:
bool myfunction (int i,int j) { return (i<j); }
std::sort (myvector.begin()+4, myvector.end(), myfunction);
I also didn't understand the following:
struct classcomp {
bool operator() (const int& lhs, const int& rhs) const
{return lhs<rhs;}
};
this keyword operator (not being followed by an operator as in a op. overload)... what is the meaning of it? Any operator applied there will have that behavior? And this const modifier... what is the effect caused by it?
I was trying to make a set of C-style string as follows:
typedef struct
{
char grid[7];
} wrap;
bool compare(wrap w1, wrap w2)
{
return strcmp(w1.grid, w2.grid) == -1;
}
set <wrap, compare> myset;
I thought I could create a set defining my sorting function in a similar as when I call sort from algorithm library... once it didn't compile I went to the documentation and saw this syntax that got me confused... Do I need to declare a pointer to a function as in the first example i pasted here?
struct classcomp {
bool operator() (const int& lhs, const int& rhs) const
{return lhs<rhs;}
};
Defines a functor by overloading the function call operator. To use a function you can do:
int main() {
std::set <wrap, bool (*)(wrap,wrap)> myset(compare);
return 0;
}
Another alternative is to define the operator as a part of the wrap class:
struct wrap {
char grid[7];
bool operator<(const wrap& rhs) const {
return strcmp(this->grid, rhs.grid) == -1;
}
};
int main() {
wrap a;
std::set <wrap> myset;
myset.insert(a);
return 0;
}
You're almost there... here's a "fixed" version of your code (see it run here at ideone.com):
#include <iostream>
#include <set>
#include <cstring>
using namespace std;
typedef struct
{
char grid[7];
} wrap;
bool compare(wrap w1, wrap w2) // more efficient: ...(const wrap& e1, const wrap# w2)
{
return strcmp(w1.grid, w2.grid) < 0;
}
set <wrap, bool(*)(wrap, wrap)> myset(compare);
int main() {
wrap w1 { "abcdef" };
wrap w2 { "ABCDEF" };
myset.insert(w1);
myset.insert(w2);
std::cout << myset.begin()->grid[0] << '\n';
}
"explain [to] me what is going on in this example"
Well, the crucial line is...
std::set<wrap, bool(*)(wrap, wrap)> myset(compare);
...which uses the second template parameter to specify the type of function that will perform comparisons, then uses the constructor argument to specify the function. The set object will store a pointer to the function, and invoke it when it needs to compare elements.
"the example for the sort method in algorithm library..."
std::sort in algorithm is great for e.g. vectors, which aren't automatically sorted as elements are inserted but can be sorted at any time. std::set though needs to maintain sorted order constantly, as the logic for inserting new elements, finding and erasing existing ones etc. all assumes the existing elements are always sorted. Consequently, you can't apply std::sort() to an existing std::set.
"this keyword operator (not being followed by an operator as in a op. overload)... what is the meaning of it? Any operator applied there will have that behavior? And this const modifier... what is the effect caused by it?
operator()(...) can be invoked on the object using the same notation used to call a function, e.g.:
classcomp my_classcomp;
if (my_classcomp(my_int1, my_int_2))
std::cout << "<\n";
As you can see, my_classcomp is "called" as if it were a function. The const modifier means that the code above works even if my_classcomp is defined as a const classcomp, because the comparison function does not need to modify any member variables of the classcomp object (if there were any data members).
You almost answered your question:
bool compare(wrap w1, wrap w2)
{
return strcmp(w1.grid, w2.grid) == -1;
}
struct wrap_comparer
{
bool operator()(const wrap& _Left, const wrap& _Right) const
{
return strcmp(_Left.grid, _Right.grid) == -1;
}
};
// declares pointer to function
bool(*fn_pt)(wrap,wrap) = compare;
// uses constructor with function pointer argument
std::set<wrap,bool(*)(wrap,wrap)> new_set(fn_pt);
// uses the function directly
std::set<wrap,bool(*)(wrap,wrap)> new_set2(compare);
// uses comparer
std::set<wrap, wrap_comparer> new_set3;
std::sort can use either a function pointer or a function object (http://www.cplusplus.com/reference/algorithm/sort/), as well as std::set constructor.
const modifier after function signature means that function can't modify object state and so can be called on a const object.

Arranging elements in a vector by their status

Let's have simplified class:
class A
{
bool val_;
public:
A() : val_(true) {}
bool isNew() const { return val_; }
void setDirty() { val_ = false; }
};
and the vector of objects of such class:
vector<A> coll;
coll.push_back(A());
coll.push_back(A());
coll.push_back(A());
coll.push_back(A());
coll[1].setDirty();
coll[3].setDirty();
I need some elegant solution to rearrange(sort) elements in the vector, so that not modified objects will be grouped at the beginning of the sequence.
You can use Partition algorithm from standard library for that:
bool MyPredicate(A& a) { return a.isNew();}
...
// bound is iterator pointing to the first element for which predicate returns false
vector<A>::iterator bound = partition(coll.begin(), coll.end(), MyPredicate);
Or, as Christian Rau suggested solution without separate function:
std::partition(coll.begin(), coll.end(), std::mem_fun_ref(&A::isNew))
How about sort:
#include <algorithm>
std::sort(coll.begin(), coll.end(),
[](const A & a, const A & b) -> bool { return a.isNew() < b.isNew(); } );
You'll have to rewrite the class to declare isNew() as const.
For older compilers, use a function instead of the lambda:
bool isNewCompare(const A & a, const A & b) { return a.isNew() < b.isNew(); }
std::sort(coll.begin(), coll.end(), isNewCompare);
Edit: #Vladimir has the better answer, std::partition() is the more appropriate algorithm for this problem.
std::sort lets you provide a custom comparison function object. You define a class that overrides the paranthesis operator, and returns true if the first argument should come before the right argument:
class COrderByDirty
{
bool operator(const A& lhs, const A& rhs) const
{
// Says lhs should come before rhs only if
// lhs is marked as dirty, and rhs is not
if (lhs.GetDirty() < rhs.Dirty())
{
return true;
}
}
}
Then simply instantiate it use it to sort:
std::sort(coll.begin(), coll.end(), COrderByDirty());
If you can use C++11, you can avoid the lengthy class creation and use a lambda, as Kernek does in his answer.
You could use std::sort from <algorithm> together with boost::bind. It could look something like this:
std::sort(coll.begin(), coll.end(), boost::bind(&A::isDirty, _1));
Assuming A has a function bool A::isDirty() const.
This works because you use the following ordering predicate implicitly:
bool cmp(const A &a, const A &b) {
return a.isDirty();
}
We just don't care what happens when both are dirty or both are not dirty.