I need to be able to sort the vector of objects by name, size and other properties while keeping them grouped together (e.g. by the group identifier mentioned above).
Actually, what I'm attempting to do is the reverse. First, I want to sort the vector by a secondary attribute (such as name, size, etc.) and then ensure that all vector elements are contained within groups.
The outcome should be the same no matter how you think about it, unless you want to use the vector in the intermediary state where it isn't sorted by group identifier. If it isn't so, I can't see why a comparison function using two criteria cannot be used (where group id is primary condition and name/size/other is secondary). You can even create a generic comparison object that combines the use of two predicates (by_two_criteria):
#include <vector>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <cstdlib>
template <class FirstCondition, class SecondCondition>
class by_two_criteria_t
{
FirstCondition first;
SecondCondition second;
public:
by_two_criteria_t(FirstCondition f, SecondCondition s): first(f), second(s) {}
template <class T>
bool operator()(const T& a, const T& b) const
{
return first(a, b) || (!first(b, a) && second(a, b));
}
};
template <class FirstCondition, class SecondCondition>
by_two_criteria_t<FirstCondition, SecondCondition> by_two_criteria(FirstCondition f, SecondCondition s)
{
return by_two_criteria_t<FirstCondition, SecondCondition>(f, s);
}
class X
{
int group;
int value;
public:
X(int g, int n): group(g), value(n) {}
friend bool compare_group(const X& a, const X& b);
friend bool compare_value(const X& a, const X& b);
friend std::ostream& operator<<(std::ostream& os, const X& x) { return os << x.group << ", " << x.value; }
};
bool compare_group(const X& a, const X& b) { return a.group < b.group; }
bool compare_value(const X& a, const X& b) { return a.value < b.value; }
X random_x()
{
return X(rand() % 10, rand() % 20);
}
int main()
{
using namespace std;
vector<X> vec;
generate_n(back_inserter(vec), 100, random_x);
sort(vec.begin(), vec.end(), by_two_criteria(compare_group, compare_value));
copy(vec.begin(), vec.end(), ostream_iterator<X>(cout, "\n"));
}
And just for fun, here's a functor that combines n comparison criteria (C++0x only for variadic templates and new-style initialization syntax):
#include <functional>
template <class T, class ...Fun>
class n_criteria_t {};
template <class T, class Fun1, class ...FunN>
class n_criteria_t<T, Fun1, FunN...>: public std::binary_function<T, T, bool>
{
public:
n_criteria_t(Fun1 f1, FunN... fn): f1(f1), f2(fn...) {}
bool operator() (const T& a, const T& b) const
{
return f1(a, b) || (!f1(b, a) && f2(a, b));
}
private:
Fun1 f1;
n_criteria_t<T, FunN...> f2;
};
template <class T, class Fun1>
class n_criteria_t<T, Fun1>: public std::binary_function<T, T, bool>
{
public:
n_criteria_t(Fun1 f1): f1(f1) {}
bool operator() (const T& a, const T& b) const
{
return f1(a, b);
}
private:
Fun1 f1;
};
template <class T, class ...Fun>
n_criteria_t<T, Fun...> n_criteria(Fun... f)
{
return {f...};
}