Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write complex sort in python?

Tags:

python

Is there a concise way to sort a list by first sorting numbers in ascending order and then sort the characters in descending order?

How would you sort the following:

['2', '4', '1', '6', '7', '4', '2', 'K', 'A', 'Z', 'B', 'W']

To:

['1', '2', '2', '4', '4', '6', '7', 'Z', 'W', 'K', 'B', 'A']

like image 228
André Krosby Avatar asked Dec 05 '22 08:12

André Krosby


1 Answers

One way (there might be better ones) is to separate digits and letters beforehand, sort them appropriately and glue them again together in the end:

lst = ['2', '4', '1', '6', '7', '4', '2', 'K', 'A', 'Z', 'B', 'W']

numbers = sorted([number for number in lst if number.isdigit()])
letters = sorted([letter for letter in lst if not letter.isdigit()], reverse=True)
combined = numbers + letters
print(combined)

Another way makes use of ord(...) and the ability to sort by tuples. Here we use zero for numbers and one for letters:

def sorter(item):
    if item.isdigit():
        return 0, int(item)
    else:
        return 1, -ord(item)

print(sorted(lst, key=sorter))

Both will yield

['1', '2', '2', '4', '4', '6', '7', 'Z', 'W', 'K', 'B', 'A']

As for timing:

def different_lists():
    global my_list
    numbers = sorted([number for number in my_list if number.isdigit()])
    letters = sorted([letter for letter in my_list if not letter.isdigit()], reverse=True)
    return numbers + letters

def key_function():
    global my_list
    def sorter(item):
        if item.isdigit():
            return 0, int(item)
        else:
            return 1, -ord(item)

    return sorted(my_list, key=sorter)

from timeit import timeit
print(timeit(different_lists, number=10**6))
print(timeit(key_function, number=10**6))

This yields (running it a million times on my MacBook):

2.9208732349999997
4.54283629

So the approach with list comprehensions is faster here.

like image 66
Jan Avatar answered Dec 10 '22 09:12

Jan