Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort a list of lists with a custom compare function

I know there are several questions named like this, but they don't seem to work for me.

I have a list of lists, 50 times 5 elements. I want to sort this list by applying a custom compare function to each element. This function calculates the fitness of the list by which the elements shall be sorted. I created two functions, compare and fitness:

def compare(item1, item2):
    return (fitness(item1) < fitness(item2))

and

def fitness(item):
    return item[0]+item[1]+item[2]+item[3]+item[4]

Then I tried to call them by:

sorted(mylist, cmp=compare)

or

sorted(mylist, key=fitness)

or

sorted(mylist, cmp=compare, key=fitness)

or

sorted(mylist, cmp=lambda x,y: compare(x,y))

Also I tried list.sort() with the same parameters. But in any case the functions don't get a list as an argument but a None. I have no idea why that is, coming from mostly C++ this contradicts any idea of a callback function for me. How can I sort this lists with a custom function?

Edit I found my mistake. In the chain that creates the original list one function didn't return anything but the return value was used. Sorry for the bother

like image 907
DaClown Avatar asked Mar 06 '11 20:03

DaClown


People also ask

How do you sort a list with a custom function in Python?

In Python, we can write custom sort functions that work with sort() and sorted() . The value of the key parameter should be a function that takes a single argument and returns a key for sorting purposes.

How do I sort a custom comparator?

Using a comparator, we can sort the elements based on data members. For instance, it may be on roll no, name, age, or anything else. Method of Collections class for sorting List elements is used to sort the elements of List by the given comparator.

Can you sort a list with different data types?

Python does not guarantee that the sort() function will work if a list contains items of different data types. As long as the items can be compared using the < comparison operator, an attempt will be made to sort the list. Otherwise, an error or exception may be generated.


3 Answers

Also, your compare function is incorrect. It needs to return -1, 0, or 1, not a boolean as you have it. The correct compare function would be:

def compare(item1, item2):
    if fitness(item1) < fitness(item2):
        return -1
    elif fitness(item1) > fitness(item2):
        return 1
    else:
        return 0

# Calling
list.sort(key=compare)
like image 51
Michael Mouse Avatar answered Oct 03 '22 03:10

Michael Mouse


Since the OP was asking for using a custom compare function (and this is what led me to this question as well), I want to give a solid answer here:

Generally, you want to use the built-in sorted() function which takes a custom comparator as its parameter. We need to pay attention to the fact that in Python 3 the parameter name and semantics have changed.

How the custom comparator works

When providing a custom comparator, it should generally return an integer/float value that follows the following pattern (as with most other programming languages and frameworks):

  • return a negative value (< 0) when the left item should be sorted before the right item
  • return a positive value (> 0) when the left item should be sorted after the right item
  • return 0 when both the left and the right item have the same weight and should be ordered "equally" without precedence

In the particular case of the OP's question, the following custom compare function can be used:

def compare(item1, item2):
    return fitness(item1) - fitness(item2)

Using the minus operation is a nifty trick because it yields to positive values when the weight of left item1 is bigger than the weight of the right item2. Hence item1 will be sorted after item2.

If you want to reverse the sort order, simply reverse the subtraction: return fitness(item2) - fitness(item1)

Calling sorted() in Python 2

sorted(mylist, cmp=compare)

or:

sorted(mylist, cmp=lambda item1, item2: fitness(item1) - fitness(item2))

Calling sorted() in Python 3

from functools import cmp_to_key
sorted(mylist, key=cmp_to_key(compare))

or:

from functools import cmp_to_key
sorted(mylist, key=cmp_to_key(lambda item1, item2: fitness(item1) - fitness(item2)))
like image 25
Lars Blumberg Avatar answered Oct 03 '22 04:10

Lars Blumberg


You need to slightly modify your compare function and use functools.cmp_to_key to pass it to sorted. Example code:

import functools

lst = [list(range(i, i+5)) for i in range(5, 1, -1)]

def fitness(item):
    return item[0]+item[1]+item[2]+item[3]+item[4]
def compare(item1, item2):
    return fitness(item1) - fitness(item2)

sorted(lst, key=functools.cmp_to_key(compare))

Output:

[[2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8], [5, 6, 7, 8, 9]]

Works :)

like image 28
JustAC0der Avatar answered Oct 03 '22 03:10

JustAC0der