131

In Python 2.x, I could pass custom function to sorted and .sort functions

>>> x=['kar','htar','har','ar']
>>>
>>> sorted(x)
['ar', 'har', 'htar', 'kar']
>>> 
>>> sorted(x,cmp=customsort)
['kar', 'htar', 'har', 'ar']

Because, in My language, consonents are comes with this order

"k","kh",....,"ht",..."h",...,"a"

But In Python 3.x, looks like I could not pass cmp keyword

>>> sorted(x,cmp=customsort)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'cmp' is an invalid keyword argument for this function

Is there any alternatives or should I write my own sorted function too?

Note: I simplified by using "k", "kh", etc. Actual characters are Unicodes and even more complicated, sometimes there is vowels comes before and after consonents, I've done custom comparison function, So that part is ok. Only the problem is I could not pass my custom comparison function to sorted or .sort

martineau
  • 119,623
  • 25
  • 170
  • 301
YOU
  • 120,166
  • 34
  • 186
  • 219
  • have you tried just `sorted(x)`? – SilentGhost Mar 28 '10 at 09:26
  • @SilentGhost, To make sure, I just tried again, Of course not working, because *my* original language is not in the locale list support by Operation Systems to do sorting. – YOU Mar 28 '10 at 09:49
  • 1
    You can wrap your cmp as a key function. Search the HowToSorting site for cmp_to_key. – Frank Aug 02 '12 at 13:56
  • 1
    here is something similar https://stackoverflow.com/questions/49327344/custom-compare-function-in-python3/49327441#49327441 – Eziz Durdyyev Mar 17 '18 at 01:55

6 Answers6

76

Use the key keyword and functools.cmp_to_key to transform your comparison function:

sorted(x, key=functools.cmp_to_key(customsort))
aknuds1
  • 65,625
  • 67
  • 195
  • 317
62

Use the key argument (and follow the recipe on how to convert your old cmp function to a key function).

functools has a function cmp_to_key mentioned at docs.python.org/3.6/library/functools.html#functools.cmp_to_key

caot
  • 3,066
  • 35
  • 37
Tim Pietzcker
  • 328,213
  • 58
  • 503
  • 561
  • +1, looks like the recipe give me a workaround, but I think I am going to lose some performance by passing all the comparison operators `< > = ` to middle man, since my original custom sort is written in C , it had around 1/2x speed of default sort. – YOU Mar 28 '10 at 05:26
  • 2
    (Just looked at your profile) Your company is blocking access to Google and StackOverflow? How stupid can they get? But about your response: I'd be interested in the actual performance decrease. Can you `timeit` it? – Tim Pietzcker Mar 28 '10 at 05:31
  • Yeah, For blocking Google is they want us to use goo.ne.jp (their affiliate I think), but I customize NTLM Proxy and Build a server side script in my hosting and tunneling through that. I don't know why for stackoverflow. - And Sure, I will do some benchmarks. – YOU Mar 28 '10 at 05:33
  • 4
    I've done some benchmarks, looks like around 4x slower than passing custom C compare function directly. – YOU Mar 28 '10 at 08:26
  • 2
    What if I need both a key function AND a cmp function? I want to sort a list of dictionaries by a custom key in each dictionary. ```sorted_rows = sorted(rows, key=itemgetter('name'), cmp=locale.strxfrm)``` gives TypeError: 'cmp' is an invalid keyword argument for this function, in Python 3.2 :( – Alex Bitek Dec 12 '14 at 08:07
  • @Dr.SkyLizard: I can't test this, but I guess `sorted_rows = sorted(rows, key=lambda x:locale.strxfrm(x['name']))` should do it. – Tim Pietzcker Dec 12 '14 at 08:14
  • 4
    functools has a cmp_to_key function in the standard library: https://docs.python.org/3.6/library/functools.html – Martín Fixman Feb 03 '16 at 19:05
24

A complete python3 cmp_to_key lambda example:

from functools import cmp_to_key

nums = [28, 50, 17, 12, 121]
nums.sort(key=cmp_to_key(lambda x, y: 1 if str(x)+str(y) < str(y)+str(x) else -1))

compare to common object sorting:

class NumStr:
    def __init__(self, v):
        self.v = v
    def __lt__(self, other):
        return self.v + other.v < other.v + self.v


A = [NumStr("12"), NumStr("121")]
A.sort()
print(A[0].v, A[1].v)

A = [obj.v for obj in A]
print(A)
Charlie 木匠
  • 2,234
  • 19
  • 19
19

Instead of a customsort(), you need a function that translates each word into something that Python already knows how to sort. For example, you could translate each word into a list of numbers where each number represents where each letter occurs in your alphabet. Something like this:

my_alphabet = ['a', 'b', 'c']

def custom_key(word):
   numbers = []
   for letter in word:
      numbers.append(my_alphabet.index(letter))
   return numbers

x=['cbaba', 'ababa', 'bbaa']
x.sort(key=custom_key)

Since your language includes multi-character letters, your custom_key function will obviously need to be more complicated. That should give you the general idea though.

Daniel Stutzbach
  • 74,198
  • 17
  • 88
  • 77
  • Thanks +1, thats ICU way I think. but since *my* language don't have word seperators and don't have standard romanize rules, it will take time to research I think. – YOU Mar 28 '10 at 05:38
  • For some other specific examples from people who didn't want to use `cmp_to_key`, see e.g. https://stackoverflow.com/questions/2531952, https://stackoverflow.com/questions/37603757, https://stackoverflow.com/questions/68968534. – Karl Knechtel Aug 15 '22 at 01:16
4

I don't know if this will help, but you may check out the locale module. It looks like you can set the locale to your language and use locale.strcoll to compare strings using your language's sorting rules.

Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • Thats true for popular languages but *my* language is not fully supported by Operation Systems, ICU, and unicode.org, so thats out of question, but +1 for good suggestion. – YOU Mar 28 '10 at 07:20
-3

Use the key argument instead. It takes a function that takes the value being processed and returns a single value giving the key to use to sort by.

sorted(x, key=somekeyfunc)
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • 4
    key only accept one parameter function, cmp have 2 parameters, they are different behavior. and I just tested, got error, because of key keyword only pass one parameter, `TypeError: customsort() takes exactly 2 positional arguments (1 given)` – YOU Mar 28 '10 at 05:17