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.
Related
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()) {}
How can I sort a list of classes by a certain member variable?
class Klasse {
int _a;
int _b;
}
...
list<Klasse> liste;
liste.sort(); // sorts by _a
liste.sort(?); // how to sort by _b now?
You would use a comparator object. Here's an example using a lambda.
std::list<Klasse> liste;
liste.sort([](Klasse const & lhs, Klasse const & rhs) {
return lhs._b < rhs._b;
});
See the reference.
You can write a comparison function - basically anything that can be called with two arguments of the element type of your list, and this call returns value convertible to bool. Such "anything" can be a lambda, function object, or simply just a function:
bool klasse_sort_by_b(const Klasse& l, const Klasse& r)
{
return l._b < r._b;
}
liste.sort(klasse_sort_by_b);
you need this implementation of sort:
template<typename Compare>
void sort (Compare comp);
then pass inside a compare function like:
bool compareByA( const Klasse& first, const Klasse& second ){
return first._a < second._a;
}
then call it:
std::list<Klasse> lst;
...
lst.sort(compareByA);
lst.sort(compareByB);
http://www.cplusplus.com/reference/list/list/sort/
You should write your own comparer, example and usage in the link ;)
here is the code example as promised
(thanks for the constructive criticism)
bool compare_by_b (const Klasse& first, const Klasse& second)
{
return first._b < second._b ;
}
liste.sort(compare_by_b);
Yes, and all you have to do is implement a comparator class, or overload the comparison Klasse::operator< operators. For reference on the sort method, see this.
This is something that I've been considering for a while. I've done some research and can't find anything on it, but I haven't found anything to the contrary either.
Consider the std::sort function in <algorithm>. It takes two iterators and a function pointer as arguments. So if I wanted to sort a vector of strings alphabetically, I would do something like this:
bool ascending(std::string lhs, std::string rhs) { return lhs < rhs; }
std::sort(my_vector.begin(), my_vector.end(), ascending);
The thing is that this type of sort function is case-sensitive, so would place a string beginning with lowercase 'a' after strings beginning with uppercase 'Z'. The only visible solution I see to this is creating an additional function along the lines of bool ascending_case_insensitive(). However, it would be nice if I could have a function bool ascending() with an additional bool is_case_sensitive parameter to use in sort. Is this possible?
Where you now have
bool ascending(std::string lhs, std::string rhs);
std::sort(my_vector.begin(), my_vector.end(), ascending);
you can have
bool ascending(std::string lhs, std::string rhs, bool case_sensitive);
using namespace std::placeholders;
std::sort(my_vector.begin(), my_vector.end(), std::bind(ascending, _1, _2, false));
The point of std::bind is to return an object that when invoked, calls the bound function, optionally with altered arguments. You can use it to change argument order, add optional parameters, or set parameters to specific fixed values.
Since std::sort takes an instance of the comparison functor, you can use arguments to your functor's constructor determine its behaviour. For example,
class StringCompare
{
public:
StringCompare(bool is_case_sensitive=true) : is_case_sensitive(is_case_sensitive){}
bool operator()(const string&, const string&);///This would handle the comparison using the is_case_sensitive flag
private:
bool is_case_sensitive;
};
std::sort(my_vector.begin(), my_vector.end(), StringCompare(true));//case-sensitive comparison
std::sort(my_vector.begin(), my_vector.end(), StringCompare(false));//case-insensitive comparison
There follows an example that includes a function call with a bound extra parameter and a lambda expression that captures the extra parameter by value:
#include <iostream>// for std::cout
#include <vector>// for std::vector
#include <functional> // for std::bind
#include <algorithm> // for std::sort
bool ltMod(int i, int j, int iMod) {
return (i % iMod) < (j % iMod);
}
int main() {
std::vector<int> v = {3,2,5,1,4};
int iMod = 4;
std::cout << "\nExample for the usage of std::bind: ";
// _1 and _2 stand for the two arguments of the relation iMod is the bound parameter
std::sort(v.begin(),v.end(),std::bind(ltMod,std::placeholders::_1,std::placeholders::_2,iMod));
for( auto i : v ) std::cout << i << ',';
iMod = 3;
std::cout << "\nExample for lambda: ";
// lambdas are unnamed inplace functions
// iMod is captured by value. You can use the value within the function.
std::sort(v.begin(),v.end(),[iMod](int i, int j){ return ltMod(i,j,iMod); });
for( auto i : v ) std::cout << i << ',';
return 0;
}
/**
Local Variables:
compile-command: "g++ -std=c++11 test.cc -o a.exe"
End:
*/
Thought that I would answer my own question in order to summarize the responses I've gotten. So from what I gather, I basically have two options.
The first would be to write a lambda function to handle my one-time case.
// Lambda solution.
std::sort(my_vector.begin(), my_vector.end(),
[](std::string const &lhs, std::string const &rhs) // Thanks for optimizing my example code guys. No, seriously. ;)
{
return boost::toupper(lhs) < boost::toupper(rhs);
});
The second, more reusable option would be to create a functor to handle sort situations like these.
// Functor solution.
class SortAscending
{
private:
bool _is_case_sensitive;
public:
SortAscending(bool is_case_sensitive) :
_is_case_sensitive(is_case_sensitive);
bool operator()(std::string const &lhs, std::string const &rhs)
{
if (_is_case_sensitive)
return boost::toupper(lhs) < boost::toupper(rhs);
else
return lhs < rhs;
}
};
std::sort(my_vector.begin(), my_vector.end(), SortAscending(false));
So think that pretty much sums up my options?
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.
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>());