如何对具有两个键但其中一个键按相反顺序的列表进行排序?

2025-01-09 08:47:00
admin
原创
88
摘要:问题描述:我想知道用 Python 的方法来按两个键对元组列表进行排序,即用一个(且只有一个)键排序将按相反顺序进行,而用另一个键排序将不区分大小写。更具体地说,我有一个包含如下元组的列表:myList = [(ele1A, ele2A),(ele1B, ele2B),(ele1C, ele2C)] 我可以使...

问题描述:

我想知道用 Python 的方法来按两个键对元组列表进行排序,即用一个(且只有一个)键排序将按相反顺序进行,而用另一个键排序将不区分大小写。更具体地说,我有一个包含如下元组的列表:

myList = [(ele1A, ele2A),(ele1B, ele2B),(ele1C, ele2C)]

我可以使用以下代码通过两个键对其进行排序:

sortedList = sorted(myList, key = lambda y: (y[0].lower(), y[1]))

要按相反的顺序排序我可以使用

sortedList = sorted(myList, key = lambda y: (y[0].lower(), y[1]), reverse = True)

但是这将使用两个键按相反的顺序排序。


解决方案 1:

您可以创建一个反转器类并使用它来修饰相关键。此类可用于反转任何可比较的字段。

class reversor:
    def __init__(self, obj):
        self.obj = obj

    def __eq__(self, other):
        return other.obj == self.obj

    def __lt__(self, other):
        return other.obj < self.obj

使用方式如下:

sortedList = sorted(myList, key=lambda y: (y[0].lower(), reversor(y[1])))

解决方案 2:

当我们需要对具有两个约束的列表进行排序时,将使用两个键:一个按升序排列,另一个按降序排列,在同一个列表或任何

在你的例子中,

sortedList = sorted(myList, key = lambda y: (y[0].lower(), y[1]))

您只能按一个顺序对整个列表进行排序。

您可以尝试这些并检查发生了什么:

sortedList = sorted(myList, key = lambda y: (y[0].lower(), -y[1]))
sortedList = sorted(myList, key = lambda y: (-y[0].lower(), y[1]))
sortedList = sorted(myList, key = lambda y: (-y[0].lower(), -y[1]))

解决方案 3:

方法 1

一个简单的解决方案,但可能不是最有效的,就是排序两次:第一次使用第二个元素,第二次使用第一个元素:

sortedList = sorted(sorted(myList, key=lambda (a,b):b, reverse=True), key=lambda(a,b):a)

或者分解:

tempList = sorted(myList, key=lambda (a,b):b, reverse=True)
sortedList = sorted(tempList, key=lambda(a,b):a))

方法 2

如果你的元素是数字,你可以稍微作弊一下:

sorted(myList, key=lambda(a,b):(a,1.0/b))

方法 3

我不建议使用这种方法,因为它很混乱并且该cmp关键字在 Python 3 中不可用。

另一种方法是在比较元素时交换元素:

def compare_func(x, y):
    tup1 = (x[0], y[1])
    tup2 = (x[1], y[0])
    if tup1 == tup2:
        return 0
    elif tup1 > tup2:
        return 1
    else:
        return -1

sortedList = sorted(myList, cmp=compare_func)

或者,使用 lambda 来避免编写函数:

sortedList = sorted(
    myList,
    cmp=lambda (a1, b1), (a2, b2): 0 if (a1, b2) == (a2, b1) else 1 if (a1, b2) > (a2, b1) else -1
    )

解决方案 4:

使用 Python 3 时,@KellyBundy做出了一个很好的观察,即当前Python 文档中列出的多排序方法速度非常快,可用于完成具有离散顺序的多列排序。这是一个NoneType安全的版本:

students = [
     {'idx': 0, 'name': 'john', 'grade': 'A', 'attend': 100}
    ,{'idx': 1, 'name': 'jane', 'grade': 'B', 'attend': 80}
    ,{'idx': 2, 'name': 'dave', 'grade': 'B', 'attend': 85}
    ,{'idx': 3, 'name': 'stu' , 'grade': None, 'attend': 85}
]

def key_grade(student):
    grade = student['grade']
    return grade is None, grade
def key_attend(student):
    attend = student['attend']
    return attend is None, attend
students_sorted = sorted(students, key=key_attend)
students_sorted.sort(key=key_grade, reverse=True)

笔记:

  • <variable> 为 None,检查是防御性检查,以便搜索不会因 None 值而失败

  • 尽管,这进行了多次排序调用,但它无疑是最快的多重排序方法!

我创建了一个名为multisort的新 Python 项目,它公开了三种方法:

方法描述笔记速度
多重排序根据Python 文档中的示例设计的简单单行代码multisort速度第二快但配置最灵活且易于阅读。0.0035
cmp_func模型中的多列排序java.util.Comparator合理的速度0.0138
逆转器反转器的实现-参见Black Panda的答案方法论相当缓慢0.0370

供参考:

方法速度
KellyBundy 的 Multisort0.0005
熊猫0.0079

注意:速度是 1000 行(4 列)运行 10 次的平均值。

multisort来自multisort 图书馆的示例:

from multisort import multisort
rows_sorted = multisort(rows_dict, [
        ('grade', True, lambda s:None if s is None else s.upper()),
        'attend',
], reverse=True)

但是,对于来自 Java 的开发人员来说,这里有一个类似于java.util.ComparatorPython 3 中使用示例:

from multisort import cmp_func

def cmp_student(a,b):
    k='grade'; va=a[k]; vb=b[k]
    if va != vb:
        if va is None: return -1
        if vb is None: return 1
        return -1 if va > vb else 1
    k='attend'; va=a[k]; vb=b[k]; 
    if va != vb:
        return -1 if va < vb else 1
    return 0

students_sorted = sorted(students, key=cmp_func(cmp_student))

解决方案 5:

有时,除了使用比较函数外别无选择。在 Python 2.4 中引入时,有一个cmp参数,但它在 Python 3 中被删除,以支持更高效的函数。在 Python 3.2 中,被添加到;它通过将原始对象包装在一个对象中来创建键,该对象的比较函数基于该函数。(您可以在“排序方法”的末尾看到的简单定义sorted`keycmp_to_keyfunctoolscmpcmp_to_key`

就您而言,由于小写字母相对昂贵,您可能需要进行组合:

class case_insensitive_and_2nd_reversed:
    def __init__(self, obj, *args):
        self.first = obj[0].lower()
        self.second = obj[1]
    def __lt__(self, other):
        return self.first < other.first or self.first == other.first and other.second < self.second
    def __gt__(self, other):
        return self.first > other.first or self.first == other.first and other.second > self.second
    def __le__(self, other):
        return self.first < other.first or self.first == other.first and other.second <= self.second
    def __ge__(self, other):
        return self.first > other.first or self.first == other.first and other.second >= self.second
    def __eq__(self, other):
        return self.first == other.first and self.second == other.second
    def __ne__(self, other):
        return self.first != other.first and self.second != other.second

sortedList = sorted(myList, key = case_insensitive_and_2nd_reversed)

解决方案 6:

也许是优雅但不是有效的方法:

reverse_key = functools.cmp_to_key(lambda a, b: (a < b) - (a > b))
sortedList = sorted(myList, key = lambda y: (reverse_key(y[0].lower()), y[1]))

解决方案 7:

基本理论

以下所有内容均适用于内置sorted函数和.sort列表方法。

一般来说,key排序函数可以简单地生成一个元组,其中每个元素对应于我们想要用于排序的“键”之一。这些元组将按字典顺序排序,因此会产生所需的结果 - 元素根据第一个键结果排序,按第二个键结果打破平局,依此类推。

同时,reversesorting的关键字参数可以指定按相反的顺序进行排序。这相当于正常排序,然后反转结果,但效率更高。

但是,此reverse设置适用于整个排序。它不允许按一个键升序排序,然后按另一个键降序排序,反之亦然。

示例设置

可以对包含任何类型的对象的列表进行排序,而不仅仅是嵌套列表/元组;并且可以编写以任何方式处理这些对象的关键函数 - 例如,根据特定属性的值对类的实例进行排序。为了清楚起见(即为了使用属性名称),我将设置一个简单的namedtuple并演示对实例列表进行排序的技术。

from collections import namedtuple
datum = namedtuple('datum', 'id age first last')
data = [
    datum(1, 23, 'Foo', 'Bar'),
    datum(2, 42, 'Baz', 'Quux'),
    # etc.
]

特殊情况:按两个数字键排序

要模拟反向排序,只需取数值的负数即可。因此:

# sort ascending by id, then descending by age
data.sort(key=lambda d: (d.id, -d.age))
# equivalent, but more complex:
data.sort(key=lambda d: (-d.id, d.age), reverse=True)

特殊情况:最多按一个非数字键排序

如果只有一个非数字键,选择是否使用reverse可以让我们避免只有数字键才能被否定的问题,方法是这样的:

# sort ascending by first name, then descending by id
data.sort(key=lambda d: (d.first, -d.id))
# sort ascending by age, then descending by last name
# since the name can't be negated, `reverse` is needed;
# this implies in turn that the age values should be negated.
data.sort(key=lambda d: (-d.age, d.last), reverse=True)

使用包装器来否定值

更通用的方法是创建一个包装类negated,其语义为negated(x) < negated(y)当且仅当。这是黑熊猫的答案中x >= y采用的方法。因此:

class negated: # name changed; otherwise the same
    def __init__(self, obj):
        self.obj = obj

    def __eq__(self, other):
        return other.obj == self.obj

    def __lt__(self, other):
        return other.obj < self.obj

# Sort descending by last name, then ascending by first name.
data.sort(lambda d: (negated(d.last), d.first))

更复杂:调整函数而不是值

假设存在某个现有的键函数my_key,并且我们想按其结果降序排序,然后按其他键升序排序。my_key我们可以像这样调整它,而不是重写 :

def negated_result(func):
    return lambda x: negated(func(x))

# Which now allows:
data.sort(lambda d: (negated_result(my_key)(d), d.id))

因为negated_result接受一个函数并返回一个函数,所以它也可以用作装饰器。

如果其他方法都失败了:对每个键重复排序

由于 Python 的内置排序保证稳定,我们可以简单地对第二个键进行排序,然后对第一个键进行排序:

# Sort "by my_key descending, then id ascending", by doing the steps
# the other way around.
data.sort(lambda d: d.id)
data.sort(my_key, reverse=True)

这个想法是,在应用主排序时,子排序将被保留。记住以相反的顺序执行此操作有点棘手,因此可能需要包装函数。例如:

# Use the `operator` module to avoid writing lambdas for simple accesses.
# This is not much simpler, but arguably more explicit.
from operator import attrgetter

# Give the sort orderings nicer names.
# See: https://stackoverflow.com/questions/31509401
from enum import Flag

class SortOrder(Flag):
    DESCENDING = True
    ASCENDING = False

def multi_sort(a_list, *specs):
    '''Sort by multiple, optionally reversed keys.
    specs -> a sequence of (func, bool) tuples.
             Each tuple specifies a key func to use for sorting,
             and whether or not to reverse the sort.'''
    for key, reverse in reversed(specs):
        # The enum value must be converted explicitly to work.
        a_list.sort(key=key, reverse=bool(reverse))

# Now the same sort looks like:
multi_sort(
    data, 
    (my_key, SortOrder.DESCENDING),
    (attrgetter('id'), SortOrder.ASCENDING)
)

解决方案 8:

至少在我的例子中,可以简单地调用X.sort()两次,使用不同的参数,一次反向调用,另一次不反向调用。我所要做的就是注意排序的优先级 - 最后执行优先级较高的排序。

例如,我有一个字符串列表,我想按长度从最长到最短排序,然后如果字符串长度相同,则按字母顺序排序。

这意味着:

lst = ["Bbbb", "Aaaa", "Ddd", "Cc"]
lst.sort()  # no extra arguments necessary for alphabetical sorting
# lst = ["Aaaa", "Bbbb", "Cc", "Ddd"]
lst.sort(key=len, reverse=True) # sort by length, which is higher priority, so last
# lst = ["Aaaa", "Bbbb", "Ddd", "Cc"]

解决方案 9:

data = [
    {"name": "Alok", "status": "offline"},
    {"name": "Bablu", "status": "online"},
    {"name": "ravi", "status": "offline"},
    {"name": "Rani", "status": "online"},
    {"name": "John", "status": "offline"},
    {"name": "Alice", "status": "online"},
    {"name": "smith", "status": "offline"},
    {"name": "Emma", "status": "online"},
    {"name": "David", "status": "offline"},
    {"name": "Olivia", "status": "online"}
]

sorted_data = sorted(data, key=lambda x: (-1 if x['status'] == 'online' else 1, x['name'].lower()))

print(sorted_data)

输出

[{'name': 'Alice', 'status': 'online'}, {'name': 'Bablu', 'status': 'online'}, {'name': 'Emma', 'status': 'online'}, {'name': 'Olivia', 'status': 'online'}, {'name': 'Rani', 'status': 'online'}, {'name': 'Alok', 'status': 'offline'}, {'name': 'David', 'status': 'offline'}, {'name': 'John', 'status': 'offline'}, {'name': 'ravi', 'status': 'offline'}, {'name': 'smith', 'status': 'offline'}]

此代码根据两个标准对字典列表进行排序:

  1. 首先,它按每个词典条目的状态进行排序。状态为“在线”的条目排在状态为“离线”的条目之前。

  2. 如果状态相同,则按名称字段的字母顺序排序,忽略大小写。

因此,此代码的输出将是一个列表,其中状态为“在线”的项目位于状态为“离线”的项目之前,并且在每个状态组中,名称按字母顺序排序。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用