7

I have a class A and a < comparator. How can I use them to sort an array of A in descending order?

class A {
...
};

class LessA {
   bool operator()(const A& a1, const A& a2) const {
   ...
   }
}

vector<A> v;
sort(v.begin(), v.end(), ???);

I suppose I should replace the ??? with something based on LessA, but I can't figure out what should go in there. I thought of using a lambda function, but I was looking for something shorter.

Paul Baltescu
  • 2,095
  • 4
  • 25
  • 30

5 Answers5

7

If you want to sort according to the relation defined by your LessA comparator, just pass an instance of LessA as the third argument (and, since you are using C++11, prefer the global std::begin() and std::end() functions):

std::sort(std::begin(a), std::end(a), LessA());
//                                    ^^^^^^^

Now if your LessA() expresses the < relation and you want to sort according to the opposite criterion, you could do:

std::sort(std::begin(a), std::end(a), 
    [] (A const& a1, A const& a2))
{
    return LessA()(a2, a1);
}

Another thing you could do is to let your custom comparator accept an argument that determines how it should perform the comparison:

class CompA {
    bool lessThan;
public:
    CompA(bool lessThan) : _lessThan(lessThan) { }
    bool operator()(const A& a1, const A& a2) const {
        if (_lessThan)
        {
            // return true iff a1 < a2;
        }
        else
        {
            // return true iff a1 > a2;
        }
    }
};

You could then use it this way to sort in ascending order:

std::sort(std::begin(a), std::end(a), CompA(true));

And this way to sort in descending order:

std::sort(std::begin(a), std::end(a), CompA(false));

Another possibility, given your original LessA comparator, is to use std::bind to swap the order of the arguments to your custom comparator:

LessA comp;
using namespace std::placeholders;
std::sort(std::begin(v), std::end(v), 
    std::bind(&LessA::operator(), comp, _2, _1));
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
6

Sort the range backwards:

vector<A> v;
sort(v.rbegin(), v.rend(), LessA());

rbegin, and rend give you reverse iterators.

Encapsulate if it's too confusing:

void reverse_sort(vector<A>& v) {
    sort(v.rbegin(), v.rend(), LessA());    
}

Usage:

vector<A> v;
reverse_sort(v);
Peter Wood
  • 23,859
  • 5
  • 60
  • 99
  • @Xeo What can I say, you have 30 upvotes for saying the opposite of me. 30 upvoters can't be wrong (c: – Peter Wood May 14 '13 at 19:28
  • 1
    I didn't mean to use the upvotes as an argument, I just meant my answer as an extended explanation on why I don't like this version. Sorry if it seemed like the former. – Xeo May 14 '13 at 19:41
  • @Xeo I appreciate that (c: I've encapsulated the function call, if that sweetens it at all. – Peter Wood May 14 '13 at 20:00
  • 1
    I like this one. It's not more complex than special reverse comparison type, lambda or bind. Was it possible to use `std::greater` it would be better, but IMO here it is justified to use reverse range. – zch May 14 '13 at 23:54
2

Use std::greater for the comparison functor. The default (std::less) will give you an ascending order; this will give you a descending order. (You'll need to add a using namespace std::rel_ops; (link) statement or explicitly define operator> as well.)

Example

Taken from cppreference.com

#include <algorithm>
#include <functional>
#include <array>
#include <iostream>
 
int main()
{
    std::array<int, 10> s = {5, 7, 4, 2, 8, 6, 1, 9, 0, 3}; 
 
    // sort using the default operator<
    std::sort(s.begin(), s.end());
    for (int a : s) {
        std::cout << a << " ";
    }   
    std::cout << '\n';
 
    // sort using a standard library compare function
    std::sort(s.begin(), s.end(), std::greater<int>());
    for (int a : s) {
        std::cout << a << " ";
    }   
    std::cout << '\n';
 
    // sort using a custom functor
    struct {
        bool operator()(int a, int b)
        {   
            return a < b;
        }   
    } customLess;
    std::sort(s.begin(), s.end(), customLess);
    for (int a : s) {
        std::cout << a << " ";
    }   
    std::cout << '\n';
 
    // sort using a lambda
    std::sort(s.begin(), s.end(), [](int a, int b) {
        return b < a;   
    });
    for (int a : s) {
        std::cout << a << " ";
    } 
    std::cout << '\n';
}
Community
  • 1
  • 1
Timothy Shields
  • 75,459
  • 18
  • 120
  • 173
  • How can I use `greater` in conjunction with objects of type `A`? – Paul Baltescu May 14 '13 at 19:11
  • @PaulBaltescu You should overload the `operator<` for your class `A` and then everything will work automatically if you use `std::greater`. – Timothy Shields May 14 '13 at 19:12
  • @PaulBaltescu I just made an edit. See the second paragraph in my answer for an alternative you could use. – Timothy Shields May 14 '13 at 19:17
  • @TimothyShields, `std::greater` uses `operator>`, it won't work with a class that only defines `operator<`, and the negation of less greater-or-equal, so is not a StrictWeakOrdering. – Jonathan Wakely May 14 '13 at 23:14
  • @JonathanWakely I assume this ( using rel_ops [link](http://en.cppreference.com/w/cpp/utility/rel_ops/operator_cmp)) is standard? If not then yeah you have to explicitly define the `operator>` as well. – Timothy Shields May 14 '13 at 23:24
  • @TimothyShields The answer still contains the wrong assumption that `!(ab)`. – Christian Rau May 15 '13 at 07:36
  • @ChristianRau It does not make that assumption. (1) `sort` will work just fine if you give it a `<=` comparison instead of a `<` comparison. (2) If you use `std::rel_ops` like I suggested, it will actually build the "correct" `operator>` for you. – Timothy Shields May 15 '13 at 15:27
  • @TimothyShields *"sort will work just fine if you give it a <= comparison instead of a < comparison."* - The standard (in particular 25.4 [alg.sorting]) does not agree with you, though (even if your particular implementation, or even any reasonable implementation, does): *"For algorithms other than those described in 25.4.3 to work correctly, comp has to induce a strict weak ordering on the values."*. You may suggest `rel_ops` as much as you want, still you also suggest `std::binary_negate` which is not a strict weak ordering. – Christian Rau May 15 '13 at 20:25
  • @ChristianRau Removed that part - happy? – Timothy Shields May 15 '13 at 20:39
  • @TimothyShields Yes, of course! You sound like you're not happy with a non-incorrect answer? – Christian Rau May 15 '13 at 20:45
0

Given a function lt(a, b) that implements a<b, you can create a function that implements a>=b by returning !lt(a, b). To implement >, you need to return !lt(b, a) && !(lt(a,b) || lt(b,a)).

lt(a, b) || lt(b, a) is equivalent to a!=b, so the above is equivalent to a>=b && a!=b which reduces to a>b.

However, you can probably get away with just std::not2(LessA()). That will sort with >= which will sort in descending order.

MSN
  • 53,214
  • 7
  • 75
  • 105
-1

Make the () operator of the LessA class return !(a1 < a2) and pass it in like this:

std::sort(v.begin(), v.end(), LessA());
user123
  • 8,970
  • 2
  • 31
  • 52
  • Yes, but that would sort `v` in ascending order. I want it in descending order. – Paul Baltescu May 14 '13 at 18:28
  • 2
    `!(a1 < a2)` will get you greater than, I think you want `(a2 < a1)` instead – K-ballo May 14 '13 at 18:40
  • I just negated the condition he told me is giving him backwards results and achieved that. Frankly, I can't tell which will yield correct results :p – user123 May 14 '13 at 18:41
  • @Magtheridon96 Generally speaking (there are exceptions), `!(a < b)` is equivalent to `a >= b`. You should negate the result of the hypothetical `a <=> b` operation: two items that previously compared as equal should continue to compare as equal (neither compares as less than the other). –  May 14 '13 at 18:46