Reputation: 11774
So I'm working with a few pre-existing comparators that compare certain values in two tuples and return true if the first is greater than the second, false if otherwise. Here's the code for one of them:
def cmpValue(subInfo1, subInfo2):
"""
Returns True if value in (value, work) tuple subInfo1 is GREATER than
value in (value, work) tuple in subInfo2
"""
# TODO...
if subInfo1[0] > subInfo2[0]:
return True
else:
return False
Now, I have a dictionary that has numerous tuple entries of the type being compared above. I want to sort them all in reverse order, but I don't really understand how I would accomplish that. I was thinking something like:
sortedDict = sorted(subjects, key=comparator, reverse = True)
But I don't know what to pass into the comparator because each comparator takes two arguments (subInfo1, subInfo2). I cannot change the comparator functions.
Upvotes: 70
Views: 160282
Reputation: 51034
In Python 3 there is no cmp
argument for the sorted
function (nor for list.sort
).
From the docs, the signature is now sorted(iterable, /, *, key=None, reverse=False)
, so you have to use a key
function to do a custom sort. The docs suggest:
Use
functools.cmp_to_key()
to convert an old-style cmp function to a key function.
Here's an example:
>>> def compare(x, y):
... return x[0] - y[0]
...
>>> data = [(4, None), (3, None), (2, None), (1, None)]
>>> from functools import cmp_to_key
>>> sorted(data, key=cmp_to_key(compare))
[(1, None), (2, None), (3, None), (4, None)]
However, your function doesn't conform to the old cmp
function protocol either, since it returns True
or False
. For your specific situation you can do:
>>> your_key = cmp_to_key(make_comparator(cmpValue))
>>> sorted(data, key=your_key)
[(1, None), (2, None), (3, None), (4, None)]
using the make_comparator
function from @Fred Foo's answer.
Upvotes: 75
Reputation: 1964
I don't know how does the answer of @Tuan Chau work. However, it may fail in complex situations.
Consider this comparison: each element is a tuple with two dimension. Element A
is less than B
if one of two conditions met: 1. A[0]
is less than B[0]
. 2. A[0]==B[0]
and A[1]>B[0]
. Therefore (1, 2) < (2, 1)
, (1, 2) < (1, 1)
.
Try the following snippets:
from functools import cmp_to_key
class Character(tuple):
def __le__(self, other) -> bool:
if self[0] < other[0] or (self[0] == other[0] and self[1] >= other[1]):
return True
return False
def compare(x, y):
if x[0] == y[0]:
return y[1] - x[1]
return x[0] - y[0]
if __name__ == "__main__":
array = [[1, 1], [2, 1], [2, 2], [1, 2]]
print(sorted(array, key=Character)) # [[1, 1], [1, 2], [2, 1], [2, 2]]
print(sorted(array, key=cmp_to_key(compare))) # [[1, 2], [1, 1], [2, 2], [2, 1]]
As you can see, the result of class Character
is wrong.
Upvotes: 2
Reputation: 311
We can now use this to sort a 2-D array:
A.sort(key=lambda a: (a[0], -a[1]))
This will sort the 2d-array by ascending order of A[0] and descending order of A[1].
Upvotes: -2
Reputation: 1333
The answer of @kaya3 is correct. I just propose another implementation in which we can use boolean for the comparator.
class YourTupleComparator(tuple):
def __lt__(self, other):
return self[0] < other[0]
sorted(subjects, key=YourTupleComparator)
Upvotes: 5
Reputation: 363547
You're passing the comparator as the key
function. You should be passing it as the cmp
, wrapped in some kind of function that turns it into a proper comparator.
def make_comparator(less_than):
def compare(x, y):
if less_than(x, y):
return -1
elif less_than(y, x):
return 1
else:
return 0
return compare
sortedDict = sorted(subjects, cmp=make_comparator(cmpValue), reverse=True)
(Although actually, you should be using key functions:
sorted(subjects, operator.itemgetter(0), reverse=True)
Also note that sortedDict
will not actually be a dict
, so the name is rather confusing.)
Upvotes: 66