Sorting By Data Members

closed account (1yvXoG1T)
As the title suggests, I'm trying to find a way to sort a list based on the data members of a class.
Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Object {
    public:
        int    a;
        int    b;
        double c;
};

bool Compare(Object* first, Object* second) {
    if(first->(a, b, or c) < second->(a, b, or c) return true;
    else return false;
}

int main() {
    std::list<Object*> ObjectList;
    // Insert code to fill list with Objects
    ObjectList.sort(Compare);

}


Which variable it accesses depends on the situation. I know how to do them individually but there's got to be a better way than that.
I assume I'd have to implement a template somehow but I'm not sure how to go about it. Any suggestions?
An alternative is to use the standard sort and specify a sort predicate.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <algorithm>
#include <iostream>

struct myobject
{
	int a, b, c;
	double d, e;
};

std::ostream &operator<<(std::ostream &os, const myobject &o)
{
	os << o.a << ", " << o.b << ", " << o.c << ", " << o.d << ", " << o.e;
	return os;
}

bool sort_by_a(const myobject &a, const myobject &b) { return a.a < b.a; }
bool sort_by_b(const myobject &a, const myobject &b) { return a.b < b.b; }
bool sort_by_c(const myobject &a, const myobject &b) { return a.c < b.c; }
bool sort_by_d(const myobject &a, const myobject &b) { return a.d < b.d; }
bool sort_by_e(const myobject &a, const myobject &b) { return a.e < b.e; }

template <typename T, size_t S> size_t ArraySize(const T (&)[S]) { return S; }

int main()
{
	myobject o[] = {
		{ 1, 14, 12, 7.0, 99.0 },
		{ 41, 4, 27, 8.0, 3.0 },
		{ 12, 18, 3, 1.0, 7.0 }
	};
	std::cout << "original order" << std::endl;
	for (const myobject* p = o; p != o + ArraySize(o); ++p)
		std::cout << *p << std::endl;
	std::cout << std::endl;

	std::cout << "sort by a" << std::endl;
	std::sort(o, o + ArraySize(o), sort_by_a);
	for (const myobject* p = o; p != o + ArraySize(o); ++p)
		std::cout << *p << std::endl;
	std::cout << std::endl;

	std::cout << "sort by b" << std::endl;
	std::sort(o, o + ArraySize(o), sort_by_b);
	for (const myobject* p = o; p != o + ArraySize(o); ++p)
		std::cout << *p << std::endl;
	std::cout << std::endl;

	//...

	return 0;
}
@neurotrace: There isn't a better way. The canonical predicate is:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Object {
        int    a;
        int    b;
        double c;
};

bool operator<( const Object& lhs, const Object& rhs ) {
    if( lhs.a < rhs.a ) return true;
    if( rhs.a < lhs.a ) return false;
    if( lhs.b < rhs.b ) return true;
    if( rhs.b < rhs.a ) return false;
    return lhs.c < rhs.c;
}


Which I think is what you want, but isn't quite what your pseudocode says.

Notice that my example ONLY uses operator<. In the case of template programming
where a, b, and/or c are template types, this operator< requires the templated type
to implement only operator<.

It turns out that with boost::tuples there is a way you can shorten the amount of code
you have to write (but I doubt you are able to use boost):

1
2
3
4
5
6
7
8
9
10
11
#include <boost/tuple/tuple_comparison.hpp>

struct Object {
        int    a;
        int    b;
        double c;
};

bool operator<( const Object& lhs, const Object& rhs ) {
    return boost::tie( lhs.a, lhs.b, lhs.c ) < boost::tie( rhs.a, rhs.b, rhs.c );
}


boost::tie returns a boost::tuple, and operator< for boost::tuple is implemented
exactly as I've written above.
closed account (1yvXoG1T)
@kbw: I like that. That will at least squash down some of the code. My current system has about 8 different sort functions for each member o_0

@jsmith: That seems to be essentially what I was aiming for. The only difference I see is I don't spot a way to specify which member to sort by in that function. But the idea is the same.
You're right, I am not using boost but I'm been hearing about it recently and I think it's time to give my old friend Google a visit.

I appreciate the input :)
If you just want to compare a single element instead of all of them, then you can do as kbw suggested.
Or, if you are interested in boost, you can write the comparator using boost::bind:

1
2
3
#include <boost/bind.hpp>

std::sort( o, o + ArraySize( o ), boost::bind( &my_object::a, _1 ) < boost::bind( &my_object::a, _2 ) );


To compare by the "a" member only.
Topic archived. No new replies allowed.