简体   繁体   English

Python:如何在数字主排序中执行二次降序字母排序

[英]Python: How to perform a secondary descending alphabetic sort within a numeric primary sort

In Sorting a Python list by two criteria Fouad gave the following answer:按两个标准对 Python 列表进行排序中,福阿德给出了以下答案:

sorted(list, key=lambda x: (x[0], -x[1]))

I'd like to sort the following list primarily on the list of tuples primarily on the second item in each element in ascending order, followed by the first (alphabetic) item in descending order:我想主要在元组列表中对以下列表进行排序,主要在每个元素中的第二个项目上按升序排列,然后是第一个(字母)项目按降序排列:

[('Ayoz', 1, 18, 7), ('Aidan', 2, 4, 9), ('Alan', 2, 4, 9), ('Arlan', 5, 6, 7), ('Luke', 15, 16, 2), ('Tariq', 5, 4, 2)] 

to give the answer:给出答案:

[('Ayoz', 1, 18, 7), ('Alan', 2, 4, 9), ('Aidan', 2, 4, 9), ('Tariq', 5, 4, 2), ('Arlan', 5, 6, 7), ('Luke', 15, 16, 2)]

using the above approach if possible .如果可能,使用上述方法。 I tried我试过了

tlist = [('Ayoz', 1, 18, 7), ('Aidan', 2, 4, 9), ('Alan', 2, 4, 9), ('Arlan', 5, 6, 7), ('Luke', 15, 16, 2), ('Tariq', 5, 4, 2)]
sorted(tlist, key=lambda elem: (elem[1], -elem[0]))

but that only works when elem[0] is numeric (in this case it gives a TypeError: bad operand type for unary -: 'str')但这仅在 elem[0] 为数字时才有效(在这种情况下,它会给出 TypeError: bad operand type for unary -: 'str')

I'll be grateful for any help.我将不胜感激任何帮助。 Python version is 3.4 Python版本是3.4

The built in sorting routines in Python are stable. Python 中内置的排序例程是稳定的。 That is, if two items have the same key value, then they keep the order they had relative to each other (the one closer to the front of the list stays closer to the front).也就是说,如果两个项目具有相同的键值,那么它们将保持它们相对于彼此的顺序(靠近列表前面的那个保持更靠近前面)。 So you can sort on multiple keys using multiple sorting passes.因此,您可以使用多个排序通道对多个键进行排序。

from operator import itemgetter

tlist = [('Ayoz', 1, 18, 7), ('Aidan', 2, 4, 9), ('Alan', 2, 4, 9),
         ('Arlan', 5, 6, 7), ('Luke', 15, 16, 2), ('Tariq', 5, 4, 2)]

# sort by name in descending order
tlist.sort(key=itemgetter(0), reverse=True) 
print('pass 1:', tlist)

# sort by element 1 in ascending order.  If two items have the same value
# the names stay in the same order they had (descending order)
tlist.sort(key=itemgetter(1))
print(npass 2:', tlist)

Prints:印刷:

pass 1: [('Tariq', 5, 4, 2), ('Luke', 15, 16, 2), ('Ayoz', 1, 18, 7), ('Arlan', 5, 6, 7), ('Alan', 2, 4, 9), ('Aidan', 2, 4, 9)]

pass 2: [('Ayoz', 1, 18, 7), ('Alan', 2, 4, 9), ('Aidan', 2, 4, 9), ('Tariq', 5, 4, 2), ('Arlan', 5, 6, 7), ('Luke', 15, 16, 2)]
tlist = [('Ayoz', 1, 18, 7), ('Alan', 2, 4, 9), ('Aidan', 2, 4, 9), ('Arlan', 5, 6, 7), ('Luke', 15, 16, 2), ('Tariq', 5, 4, 2)] 

sorted(tlist, key=lambda elem: (elem[1],sorted(elem[0],reverse=True)))

Worked it out but it took me half an hour to type so I'm posting no matter what.解决了,但我花了半个小时才打字,所以无论如何我都会发帖。 I still welcome a better way of doing it.我仍然欢迎更好的方法。

您可以对原始答案进行修改以使其正常工作:

sorted(tlist, key=lambda elem: (-elem[1], elem[0]), reverse=True)

Here's an alternative.这是一个替代方案。 It works like the trick to use -number to reverse the order of a sort, but applies to strings of letters.它的工作原理类似于使用 -number 来反转排序顺序的技巧,但适用于字母字符串。

The table below maps 'a' to 'z', 'b' to 'y', etc. t[0].translate(table) translates 'Ayoz' to 'Zbla', so the key for ('Ayoz', 1, 18, 7) is (1, Zbla')下表将 'a' 映射到 'z','b' 映射到 'y' 等等。 t[0].translate(table)将 'Ayoz' 转换为 'Zbla',因此 ('Ayoz', 1 , 18, 7) 是 (1, Zbla')

table = str.maketrans('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
                      'zyxwvutsrqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA')

tlist.sort(key=lambda t: (t[1], t[0].translate(table)))

I just ran into this, and I was surprised to find that it's a missing piece in Python's otherwise robust sorting.我刚刚遇到这个问题,我惊讶地发现它是 Python 强大的排序中缺少的部分。 You can reverse the overall sort, but you can't reverse subsorts.您可以反转整体排序,但不能反转子排序。 The other answers are usable workarounds, but they aren't general solutions if you have a list of fields you want to sort with varying subsort order, especially if multiple-pass sorting isn't an option.其他答案是可用的解决方法,但如果您有一个要使用不同子排序顺序排序的字段列表,则它们不是通用解决方案,尤其是在不能选择多遍排序的情况下。

This is what I ended up with:这就是我最终的结果:

class inverse_str(str):
    """
    A string that sorts in inverse order.
    """
    def __lt__(self, rhs):
        return not super().__lt__(rhs)

data = [('Ayoz', 1, 18, 7), ('Aidan', 2, 4, 9), ('Alan', 2, 4, 9), ('Arlan', 5, 6, 7), ('Luke', 15, 16, 2), ('Tariq', 5, 4, 2)]
data.sort(key=lambda item: (item[1], inverse_str(item[0])))
print(data)

wanted = [('Ayoz', 1, 18, 7), ('Alan', 2, 4, 9), ('Aidan', 2, 4, 9), ('Tariq', 5, 4, 2), ('Arlan', 5, 6, 7), ('Luke', 15, 16, 2)]
print(data == wanted)

That has the same effect as returning eg.这与返回例如具有相同的效果。 1-item[1] from the key function for a float field.浮点字段的键函数中的 1-item[1]。

RootTwo's multi-stage sort answer is valid, but multiple-step sorting isn't always an option. RootTwo 的多级排序答案是有效的,但多步排序并不总是一种选择。 I'm merging incremental previously-sorted streams of data using heapq.merge, so that won't work.我正在使用 heapq.merge 合并增量的先前排序的数据流,所以这不起作用。 Doing it within the comparison does this within the actual ordering, so you don't need multiple passes and it works in the general case.在比较中执行此操作会在实际排序中执行此操作,因此您不需要多次传递,并且它适用于一般情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM