如何在不使用reverse = True参数的情况下以相反顺序对字符串列表进行排序?

时间:2019-04-26 11:34:51

标签: python python-3.x string list sorting

我想以相反的顺序对字符串列表进行排序,例如:

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

预期结果:

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

我不想使用sorted(my_list, reverse=True),因为在更复杂的情况下,按两个值进行过滤将无法正常工作。例如:

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

预期结果将是:

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

sorted(my_list2, reverse=True)返回:

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

数字很简单,您可以取反值:

>>> 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)]

但是如何使用字符串呢?

4 个答案:

答案 0 :(得分:7)

您必须进行两次排序。 Python的排序算法为 stable ,这意味着相等的元素保持其相对顺序。使用此方法首先对第二个元素进行排序(以升序排序),然后仅对第一个元素按相反的顺序再次对输出进行排序:

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

使用operator.itemgetter()而不是lambda可以使速度更快(避免对每个元素退回到Python解释器):

from operator import itemgetter

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

演示:

>>> 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')]

一般规则是从最内层元素到最外层元素排序。因此,对于任意元素计数排序(每个都有一个键和一个反向布尔值),可以使用functools.reduce() function来应用它们:

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))

答案 1 :(得分:1)

如果“ my_list2”仅包含ASCII,则可以尝试:

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')]

答案 2 :(得分:1)

另一种解决方案是创建一个带有比较函数的类以用作键。您只需要定义 sort/sorted 使用的 __lt__

def reverse_key(reverse):
    class C:
        def __init__(self, obj):
            self.obj = obj
        def __lt__(self, other):
            for a, b, r in zip(self.obj, other.obj, reverse):
                if a < b:
                    return not r
                elif a > b:
                    return r
            return False
    return C

这可以与 sort/sorted by 一起使用

my_list2 = [('aaa', 'bbb'), ('aaa', 'ccc'), ('bbb', 'aaa'), ('bbb', 'ccc')]
sorted(my_list2, key=reverse_key([True, False]))

正如所见,这只允许调用 sort/sorted 一次。就性能而言,如果要排序的列表中的每个元组都包含大量项目,则这可能比接受的答案更快。

答案 3 :(得分:0)

您可以对值做一个负ord,这将适用于ascii字符串:

>>> sorted(my_list2, key=lambda x: ([-ord(l) for l in x[0]], x[1]))
[('bbb', 'aaa'), ('bbb', 'ccc'), ('aaa', 'bbb'), ('aaa', 'ccc')]

对于非ascii,虽然您可以选择排序方式:

>>> sorted(my_list2, key=lambda x: ([-ord(l) for l in x[0]], x[1]))
[('ébb', 'écc'), ('bbb', 'aaa'), ('aaa', 'bbb'), ('aaa', 'ccc')]