Reputation: 403
I want to order the items in a dictionary using various comparator functions. Please see my example code below. It is the last part using cmpRatio function with sorted() that does not work. I am not sure what I am doing wrong. Thanks in advance for any idea!
mydict = { 'a1': (1,6),
'a2': (10,2),
'a3': (5,3),
'a4': (1,2),
'a5': (3,9),
'a6': (9,7) }
# sort by first element of the value tuple: WORKS
print sorted(mydict.iteritems(), key=lambda (k,v): v[0])
# sort by second element of the value tuple: WORKS
print sorted(mydict.iteritems(), key=lambda (k,v): v[1])
# THIS is what I can't get working:
def cmpRatio(x,y):
sx = float(x[0])/x[1]
sy = float(y[0])/y[1]
return sx < sy
# sort by sum of the elements in the value tuple: DOES NOT WORK
print sorted(mydict.iteritems(), key=lambda (k,v): v, cmp=cmpRatio)
Upvotes: 8
Views: 16308
Reputation: 92569
Avoid cmp
functions where possible because they are slow. They have to be re-evaluated for each comparison. Using a key
makes it so the key only needs to be computed once.
print sorted(mydict.iteritems(), key=lambda (k,v): float(v[0])/v[1])
Also, you say you want to sort by the sum of the value items, yet you are sorting by the difference. Sum would look like:
print sorted(mydict.iteritems(), key=lambda (k,v): sum(v))
As mentioned in other answers, for the purpose of really wanting to define a cmp
function, you are not returning the proper value (must be -1,0, or 1).
return cmp(sx,sy)
But also if you are just using a lambda to get the value, you can replace that with itemgetter
which should be faster than a python-side function:
from operator import itemgetter
print sorted(mydict.iteritems(), key=itemgetter(1), cmp=cmpRatio)
If you are trying to store up sort operations, it would be much better to store the key functions:
key_ops = {
'sum': lambda (k,v): sum(v),
'ratio': lambda (k,v): float(v[0])/v[1]),
}
def print_op(aDict, opName):
print sorted(aDict.iteritems(), key=key_ops[opName])
... # some place later in code
print_op(mydict, 'sum')
Upvotes: 6
Reputation: 18675
you comparison function should return a (negative / zero / positive) value when the first argument is (less than / equal to / greater than) the second value (unlike a comparator given to std::sort(...)
in C++).
i.e. instead of
return sx < sy
do
return cmp(sx,sy)
Upvotes: 4
Reputation: 2250
If you want to sort by the sum of the value tuple (according to your comment), you could use :
print sorted(mydict.iteritems(), key=lambda v: sum(v[1]))
If you want to sort by the ratio (according to your code):
print sorted(mydict.iteritems(), key=lambda v: float(v[1][0])/v[1][1])
Upvotes: 2