0
def sort_line(line1,line2):

    for x1,y1,x2,y2 in line1:
        mn1 = min(x1,x2)
    for x1,y1,x2,y2 in line2:
        mn2 = min(x1,x2)
    return mn1 < mn2 

lines.sort(sort_line)

Here lines has 4 int in every row. From what I understood from python wiki is that I need to pass a compare function as argument in the sort() function. But I get the following error,

<ipython-input-107-b0e2f3c756cf> in draw_lines(img, lines, color, thickness)
     69     """
     70 
---> 71     lines.sort(sort_line)
     72 
     73     for line in lines:

TypeError: an integer is required (got type function)

I tried with sorted() but couldn't make it work either.

Python and anaconda version

Python 3.5.2 |Anaconda 4.2.0 (64-bit)| (default, Jul 5 2016, 11:41:13) [MSC v.1900 64 bit (AMD64)] on win32

I'm running the code in jupyter notebook.

My implementation I think is same as the following question but mine doesn't work.

Custom Python list sorting

After following Martijn's answer I modified my code to this

lineList = lines.tolist()  //lines is a numpy array so I converted it to list
print ('lineList: ',lineList)
lineList.sort(key=lambda l: min(l[0], l[2]))
lines = np.array(lineList)

Gives me the following error

<ipython-input-115-06412e8f5aba> in <lambda>(l)
     72     lineList = lines.tolist()
     73     print ('lineList: ',lineList)
---> 74     lineList.sort(key=lambda l: min(l[0], l[1]))
     75     lines = np.array(lineList)
     76 

IndexError: list index out of range
Community
  • 1
  • 1
Tahlil
  • 2,680
  • 6
  • 43
  • 84
  • But doesn't the sort function requires a compare function as argument? and I'm passing the compare function in the sort function. – Tahlil Dec 04 '16 at 10:50
  • 2
    You appear to be expecting sorting to use a `cmp()` function. It does not; you are only given **one** element and are expected to give a sort key. You are getting confused with Python 2 here. – Martijn Pieters Dec 04 '16 at 10:52
  • You appear to be reading the *The Old Way Using the cmp Parameter* section on the wiki, that option is basically gone in Python 3 (or use the *slow* [`functools.cmp_to_key()` wrapper](https://docs.python.org/3/library/functools.html#functools.cmp_to_key)). – Martijn Pieters Dec 04 '16 at 10:54
  • All this *is* clearly documented in the sorting howto however, as well as in the [`list.sort()` method documentation](https://docs.python.org/3/library/stdtypes.html#list.sort). – Martijn Pieters Dec 04 '16 at 10:55

2 Answers2

3

You are making several mistakes. You didn't read the documentation close enough; the cmp function option is gone in Python 3. You also implemented your cmp function incorrectly, and last but not least, you don't need to use a cmp function at all, you can use a key function to extract the minimum of the two exact values in the lists you are sorting.

You need to pay close attention to the section you appear to be reading, The Old Way Using the cmp Parameter:

In Py3.0, the cmp parameter was removed entirely (as part of a larger effort to simplify and unify the language, eliminating the conflict between rich comparisons and the __cmp__ methods).

The error message is a little confusing here, but list.sort() takes no positional arguments. From the list.sort() documentation:

sort(*, key=None, reverse=None)

The * indicates no positional arguments are accepted, and there is no cmp option.

Going back to the Sorting Howto:

When porting code from Python 2.x to 3.x, the situation can arise when you have the user supplying a comparison function and you need to convert that to a key function. The following wrapper makes that easy to do:

def cmp_to_key(mycmp):

[...]

In Python 2.7, the cmp_to_key() tool was added to the functools module.

and again from the list.sort() documentation:

The functools.cmp_to_key() utility is available to convert a 2.x style cmp function to a key function.

You also incorrectly implemented your cmp function; there is no need to iterate, and you have to return -1, 0 or 1 to indicate relative order (so you would have to use cmp(min(list1), min(list2)) if there still was a built-in cmp() function).

However, you can sort your data without a (slow) cmp function. All you need to do is to extract the min() of the two values of each list, in a sort key:

lines.sort(key=lambda l: min(l[0], l[2]))
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Thanks very much. I understand now. I want to sort the list according to smallest x values in each row. My code for comp function was wrong and I modified it now. So for each row in list the row with smallest x (between each x1,x2 in every row) should be the first element after sort and then the next. But using min would return the smallest between all 4 elements right? In that case it wont' serve my purpose :( But thanks very much now I understand the reason of the error. – Tahlil Dec 04 '16 at 11:11
  • @Tahlil: I saw that edit and already corrected for it. Just have a function (provided by a `lambda` expression in my example) that returns the minimum of those two values. – Martijn Pieters Dec 04 '16 at 11:11
  • I'm getting array index out of bound error now with the lambda function. Edited the question with the error. I'm very new in python :( Thanks for your patience. – Tahlil Dec 04 '16 at 11:39
  • @Tahlil: then you have entries with fewer than 3 elements. Either use `lambda l: (len(l) > 2, min(l[0], l[2]))` to sort those shorter lists separately, or remove such entries from the input list first. You didn't give us any sample data to sort, so we can only *guess* at your exact data structures here. – Martijn Pieters Dec 04 '16 at 11:43
  • printing linesList shows every row has 4 elements. The output of the print just before the lambda function line is: lineList: [[[515, 327, 854, 538]], [[774, 501, 832, 539]], [[499, 320, 763, 492]], [[310, 423, 446, 331]], [[319, 425, 454, 327]], [[524, 333, 853, 539]], [[730, 472, 796, 514]], [[698, 451, 758, 489]], [[361, 392, 418, 351]], [[498, 320, 558, 359]], [[318, 425, 415, 355]], [[612, 394, 828, 535]], [[387, 370, 436, 336]]] – Tahlil Dec 04 '16 at 11:46
  • @Tahlil: no, each list has just **one** element, which is another list. You may want to fix that and remove the outer lists, they are entirely redundant. Otherwise use `lambda l: min(l[0][0], l[0][2])` to address the single list in the lists. – Martijn Pieters Dec 04 '16 at 11:47
1

According to the documentation:

sort() accepts two arguments that can only be passed by keyword (keyword-only arguments): key and reverse.

The cmp parameter only exists in Python 2, it is generally considered as a deprecated usage.

Laurent LAPORTE
  • 21,958
  • 6
  • 58
  • 103