How to sort a list of strings in reverse order without using reverse=True parameter?

笑着哭i 提交于 2020-02-25 07:53:29

问题


I want to sort a list of strings in reverse order, e.g.:

my_list = ['aaa', 'bbb', 'ccc']

expected result:

['ccc', 'bbb', 'aaa']

I don't want to use sorted(my_list, reverse=True), because in more complex case when filtering by two values it would not work. For example:

my_list2 = [('aaa', 'bbb'), ('aaa', 'ccc'), ('bbb', 'aaa'), ('bbb', 'ccc')]

expected result would be:

[('bbb', 'aaa'), ('bbb', 'ccc'), ('aaa', 'bbb'), ('aaa', 'ccc')]

sorted(my_list2, reverse=True) returns:

[('bbb', 'ccc'), ('bbb', 'aaa'), ('aaa', 'ccc'), ('aaa', 'bbb')]

It is simple with numbers, you can negate the value:

>>> my_list3 = [(1, 2), (1, 3), (2, 1), (2, 3)]
>>> sorted(my_list3, key=lambda x: (-x[0], x[1]))
... [(2, 1), (2, 3), (1, 2), (1, 3)]

But how to do it with strings?


回答1:


You'll have to sort twice. Python's sort algorithm is stable, which means that elements that are equal keep their relative order. Use this to first sort on the second element (sorting in ascending order), then sort that output again, on only the first element and in reverse order:

sorted(sorted(my_list2, key=lambda t: t[1]), key=lambda t: t[0], reverse=True)

Using operator.itemgetter() instead of lambdas can make this little bit faster (avoiding stepping back in to the Python interpreter for each element):

from operator import itemgetter

sorted(sorted(my_list2, key=itemgetter(1)), key=itemgetter(0), reverse=True)

Demo:

>>> from operator import itemgetter
>>> my_list2 = [('aaa', 'bbb'), ('aaa', 'ccc'), ('bbb', 'aaa'), ('bbb', 'ccc')]
>>> sorted(sorted(my_list2, key=lambda t: t[1]), key=lambda t: t[0], reverse=True)
[('bbb', 'aaa'), ('bbb', 'ccc'), ('aaa', 'bbb'), ('aaa', 'ccc')]
>>> sorted(sorted(my_list2, key=itemgetter(1)), key=itemgetter(0), reverse=True)
[('bbb', 'aaa'), ('bbb', 'ccc'), ('aaa', 'bbb'), ('aaa', 'ccc')]

The general rule is to sort from the innermost element to the outermost element. So for an arbitrary-element-count sort, with a key and a reverse boolean each, you can use the functools.reduce() function to apply these:

from functools import reduce
from operator import itemgetter

def sort_multiple(sequence, *sort_order):
    """Sort a sequence by multiple criteria.

    Accepts a sequence and 0 or more (key, reverse) tuples, where
    the key is a callable used to extract the value to sort on
    from the input sequence, and reverse is a boolean dictating if
    this value is sorted in ascending or descending order.

    """
    return reduce(
        lambda s, order: sorted(s, key=order[0], reverse=order[1]),
        reversed(sort_order),
        sequence
    )

sort_multiple(my_list2, (itemgetter(0), True), (itemgetter(1), False))



回答2:


If 'my_list2' contains only ASCII, you can try:

sorted(my_list2, key=lambda t: (t[0],[255-ord(c) for c in list(t[1])]), reverse=True)                                
[('bbb', 'aaa'), ('bbb', 'ccc'), ('aaa', 'bbb'), ('aaa', 'ccc')]


来源:https://stackoverflow.com/questions/55866762/how-to-sort-a-list-of-strings-in-reverse-order-without-using-reverse-true-parame

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!