如何按多个键对对象进行排序?

2025-02-28 08:23:00
admin
原创
70
摘要:问题描述:或者,实际上,我如何按多个键对字典列表进行排序?我有一份口述清单:b = [{u'TOT_PTS_Misc': u'Utley, Alex', u'Total_Points': 96.0}, {u'TOT_PTS_Misc': u'Russo, Brandon', u'Total_Points':...

问题描述:

或者,实际上,我如何按多个键对字典列表进行排序?

我有一份口述清单:

b = [{u'TOT_PTS_Misc': u'Utley, Alex', u'Total_Points': 96.0},
 {u'TOT_PTS_Misc': u'Russo, Brandon', u'Total_Points': 96.0},
 {u'TOT_PTS_Misc': u'Chappell, Justin', u'Total_Points': 96.0},
 {u'TOT_PTS_Misc': u'Foster, Toney', u'Total_Points': 80.0},
 {u'TOT_PTS_Misc': u'Lawson, Roman', u'Total_Points': 80.0},
 {u'TOT_PTS_Misc': u'Lempke, Sam', u'Total_Points': 80.0},
 {u'TOT_PTS_Misc': u'Gnezda, Alex', u'Total_Points': 78.0},
 {u'TOT_PTS_Misc': u'Kirks, Damien', u'Total_Points': 78.0},
 {u'TOT_PTS_Misc': u'Worden, Tom', u'Total_Points': 78.0},
 {u'TOT_PTS_Misc': u'Korecz, Mike', u'Total_Points': 78.0},
 {u'TOT_PTS_Misc': u'Swartz, Brian', u'Total_Points': 66.0},
 {u'TOT_PTS_Misc': u'Burgess, Randy', u'Total_Points': 66.0},
 {u'TOT_PTS_Misc': u'Smugala, Ryan', u'Total_Points': 66.0},
 {u'TOT_PTS_Misc': u'Harmon, Gary', u'Total_Points': 66.0},
 {u'TOT_PTS_Misc': u'Blasinsky, Scott', u'Total_Points': 60.0},
 {u'TOT_PTS_Misc': u'Carter III, Laymon', u'Total_Points': 60.0},
 {u'TOT_PTS_Misc': u'Coleman, Johnathan', u'Total_Points': 60.0},
 {u'TOT_PTS_Misc': u'Venditti, Nick', u'Total_Points': 60.0},
 {u'TOT_PTS_Misc': u'Blackwell, Devon', u'Total_Points': 60.0},
 {u'TOT_PTS_Misc': u'Kovach, Alex', u'Total_Points': 60.0},
 {u'TOT_PTS_Misc': u'Bolden, Antonio', u'Total_Points': 60.0},
 {u'TOT_PTS_Misc': u'Smith, Ryan', u'Total_Points': 60.0}]

我需要使用多键排序,先按 Total_Points 反转,然后再按 反转TOT_PTS_Misc

这可以在命令提示符下完成,如下所示:

a = sorted(b, key=lambda d: (-d['Total_Points'], d['TOT_PTS_Misc']))

但我必须通过一个函数来运行它,在其中我传入列表和排序键。例如,def multikeysort(dict_list, sortkeys):

对于传递给 multikeysort 函数的任意数量的键,如何使用 lambda 行对列表进行排序,并考虑到 sortkeys 可能有任意数量的键,而那些需要反向排序的键将在其前面用“-”标识?


解决方案 1:

这篇文章对实现此目的的各种技术进行了很好的概述。如果您的要求比“完全双向多密钥”更简单,请看一下。很明显,接受的答案和我刚刚引用的博客文章在某种程度上相互影响,尽管我不知道是哪种顺序。

如果链接失效,下面是上面未涉及的示例的简要概述:

mylist = sorted(mylist, key=itemgetter('name', 'age'))
mylist = sorted(mylist, key=lambda k: (k['name'].lower(), k['age']))
mylist = sorted(mylist, key=lambda k: (k['name'].lower(), -k['age']))

解决方案 2:

这个答案适用于字典中的任何类型的列——否定的列不必是数字。

def multikeysort(items, columns):
    from operator import itemgetter
    comparers = [((itemgetter(col[1:].strip()), -1) if col.startswith('-') else
                  (itemgetter(col.strip()), 1)) for col in columns]
    def comparer(left, right):
        for fn, mult in comparers:
            result = cmp(fn(left), fn(right))
            if result:
                return mult * result
        else:
            return 0
    return sorted(items, cmp=comparer)

你可以这样调用它:

b = [{u'TOT_PTS_Misc': u'Utley, Alex', u'Total_Points': 96.0},
     {u'TOT_PTS_Misc': u'Russo, Brandon', u'Total_Points': 96.0},
     {u'TOT_PTS_Misc': u'Chappell, Justin', u'Total_Points': 96.0},
     {u'TOT_PTS_Misc': u'Foster, Toney', u'Total_Points': 80.0},
     {u'TOT_PTS_Misc': u'Lawson, Roman', u'Total_Points': 80.0},
     {u'TOT_PTS_Misc': u'Lempke, Sam', u'Total_Points': 80.0},
     {u'TOT_PTS_Misc': u'Gnezda, Alex', u'Total_Points': 78.0},
     {u'TOT_PTS_Misc': u'Kirks, Damien', u'Total_Points': 78.0},
     {u'TOT_PTS_Misc': u'Worden, Tom', u'Total_Points': 78.0},
     {u'TOT_PTS_Misc': u'Korecz, Mike', u'Total_Points': 78.0},
     {u'TOT_PTS_Misc': u'Swartz, Brian', u'Total_Points': 66.0},
     {u'TOT_PTS_Misc': u'Burgess, Randy', u'Total_Points': 66.0},
     {u'TOT_PTS_Misc': u'Smugala, Ryan', u'Total_Points': 66.0},
     {u'TOT_PTS_Misc': u'Harmon, Gary', u'Total_Points': 66.0},
     {u'TOT_PTS_Misc': u'Blasinsky, Scott', u'Total_Points': 60.0},
     {u'TOT_PTS_Misc': u'Carter III, Laymon', u'Total_Points': 60.0},
     {u'TOT_PTS_Misc': u'Coleman, Johnathan', u'Total_Points': 60.0},
     {u'TOT_PTS_Misc': u'Venditti, Nick', u'Total_Points': 60.0},
     {u'TOT_PTS_Misc': u'Blackwell, Devon', u'Total_Points': 60.0},
     {u'TOT_PTS_Misc': u'Kovach, Alex', u'Total_Points': 60.0},
     {u'TOT_PTS_Misc': u'Bolden, Antonio', u'Total_Points': 60.0},
     {u'TOT_PTS_Misc': u'Smith, Ryan', u'Total_Points': 60.0}]

a = multikeysort(b, ['-Total_Points', 'TOT_PTS_Misc'])
for item in a:
    print item

尝试对任一列取反。您将看到排序顺序反转。

下一步:改变它,使其不使用额外的类....


2016-01-17

我从这个答案“从符合条件的可迭代对象中获取第一个项目的最佳方法是什么?”中获得灵感,我缩短了代码:

from operator import itemgetter as i

def multikeysort(items, columns):
    comparers = [
        ((i(col[1:].strip()), -1) if col.startswith('-') else (i(col.strip()), 1))
        for col in columns
    ]
    def comparer(left, right):
        comparer_iter = (
            cmp(fn(left), fn(right)) * mult
            for fn, mult in comparers
        )
        return next((result for result in comparer_iter if result), 0)
    return sorted(items, cmp=comparer)

如果你喜欢简洁的代码。


稍后 2016-01-17

这适用于 python3 (消除了 的cmp参数sort):

from operator import itemgetter as i
from functools import cmp_to_key

def cmp(x, y):
    """
    Replacement for built-in function cmp that was removed in Python 3

    Compare the two objects x and y and return an integer according to
    the outcome. The return value is negative if x < y, zero if x == y
    and strictly positive if x > y.

    https://portingguide.readthedocs.io/en/latest/comparisons.html#the-cmp-function
    """

    return (x > y) - (x < y)

def multikeysort(items, columns):
    comparers = [
        ((i(col[1:].strip()), -1) if col.startswith('-') else (i(col.strip()), 1))
        for col in columns
    ]
    def comparer(left, right):
        comparer_iter = (
            cmp(fn(left), fn(right)) * mult
            for fn, mult in comparers
        )
        return next((result for result in comparer_iter if result), 0)
    return sorted(items, key=cmp_to_key(comparer))

受到这个答案的启发,我应该如何在 Python 3 中进行自定义排序?

解决方案 3:

我知道这是一个相当老的问题,但没有一个答案提到 Python 保证其排序例程(如list.sort()sorted())的稳定排序顺序,这意味着比较相等的项目将保留其原始顺序。

这意味着ORDER BY name ASC, age DESC对于字典列表(使用 SQL 符号)可以像这样完成:

items.sort(key=operator.itemgetter('age'), reverse=True)
items.sort(key=operator.itemgetter('name'))

请注意,这些项目首先按“较小”属性(降序)排序age,然后按“主要”属性排序name,从而得到正确的最终顺序。

反转/倒置适用于所有可排序类型,而不仅仅是可以通过在前面加上减号来取反的数字。

并且由于 (至少) CPython 中使用了 Timsort 算法,因此在实践中这实际上相当快。

解决方案 4:

def sortkeypicker(keynames):
    negate = set()
    for i, k in enumerate(keynames):
        if k[:1] == '-':
            keynames[i] = k[1:]
            negate.add(k[1:])
    def getit(adict):
       composite = [adict[k] for k in keynames]
       for i, (k, v) in enumerate(zip(keynames, composite)):
           if k in negate:
               composite[i] = -v
       return composite
    return getit

a = sorted(b, key=sortkeypicker(['-Total_Points', 'TOT_PTS_Misc']))

解决方案 5:

我今天遇到了类似的问题 - 我必须按降序排列数字值,按升序排列字符串值来对字典项进行排序。为了解决方向冲突的问题,我对整数值取反。

这是我的解决方案的一个变体 - 适用于 OP

sorted(b, key=lambda e: (-e['Total_Points'], e['TOT_PTS_Misc']))

非常简单 - 而且非常有效

[{'TOT_PTS_Misc': 'Chappell, Justin', 'Total_Points': 96.0},
 {'TOT_PTS_Misc': 'Russo, Brandon', 'Total_Points': 96.0},
 {'TOT_PTS_Misc': 'Utley, Alex', 'Total_Points': 96.0},
 {'TOT_PTS_Misc': 'Foster, Toney', 'Total_Points': 80.0},
 {'TOT_PTS_Misc': 'Lawson, Roman', 'Total_Points': 80.0},
 {'TOT_PTS_Misc': 'Lempke, Sam', 'Total_Points': 80.0},
 {'TOT_PTS_Misc': 'Gnezda, Alex', 'Total_Points': 78.0},
 {'TOT_PTS_Misc': 'Kirks, Damien', 'Total_Points': 78.0},
 {'TOT_PTS_Misc': 'Korecz, Mike', 'Total_Points': 78.0},
 {'TOT_PTS_Misc': 'Worden, Tom', 'Total_Points': 78.0},
 {'TOT_PTS_Misc': 'Burgess, Randy', 'Total_Points': 66.0},
 {'TOT_PTS_Misc': 'Harmon, Gary', 'Total_Points': 66.0},
 {'TOT_PTS_Misc': 'Smugala, Ryan', 'Total_Points': 66.0},
 {'TOT_PTS_Misc': 'Swartz, Brian', 'Total_Points': 66.0},
 {'TOT_PTS_Misc': 'Blackwell, Devon', 'Total_Points': 60.0},
 {'TOT_PTS_Misc': 'Blasinsky, Scott', 'Total_Points': 60.0},
 {'TOT_PTS_Misc': 'Bolden, Antonio', 'Total_Points': 60.0},
 {'TOT_PTS_Misc': 'Carter III, Laymon', 'Total_Points': 60.0},
 {'TOT_PTS_Misc': 'Coleman, Johnathan', 'Total_Points': 60.0},
 {'TOT_PTS_Misc': 'Kovach, Alex', 'Total_Points': 60.0},
 {'TOT_PTS_Misc': 'Smith, Ryan', 'Total_Points': 60.0},
 {'TOT_PTS_Misc': 'Venditti, Nick', 'Total_Points': 60.0}]

解决方案 6:

我使用以下内容对多列上的二维数组进行排序

def k(a,b):
    def _k(item):
        return (item[a],item[b])
    return _k

这可以扩展到处理任意数量的项目。我倾向于认为找到更好的可排序键访问模式比编写花哨的比较器更好。

>>> data = [[0,1,2,3,4],[0,2,3,4,5],[1,0,2,3,4]]
>>> sorted(data, key=k(0,1))
[[0, 1, 2, 3, 4], [0, 2, 3, 4, 5], [1, 0, 2, 3, 4]]
>>> sorted(data, key=k(1,0))
[[1, 0, 2, 3, 4], [0, 1, 2, 3, 4], [0, 2, 3, 4, 5]]
>>> sorted(a, key=k(2,0))
[[0, 1, 2, 3, 4], [1, 0, 2, 3, 4], [0, 2, 3, 4, 5]]

解决方案 7:

from operator import itemgetter
from functools import partial

def _neg_itemgetter(key, d):
    return -d[key]

def key_getter(key_expr):
    keys = key_expr.split(",")
    getters = []
    for k in keys:
        k = k.strip()
        if k.startswith("-"):
           getters.append(partial(_neg_itemgetter, k[1:]))
        else:
           getters.append(itemgetter(k))

    def keyfunc(dct):
        return [kg(dct) for kg in getters]

    return keyfunc

def multikeysort(dict_list, sortkeys):
    return sorted(dict_list, key = key_getter(sortkeys)

示范:

>>> multikeysort([{u'TOT_PTS_Misc': u'Utley, Alex', u'Total_Points': 60.0},
                 {u'TOT_PTS_Misc': u'Russo, Brandon', u'Total_Points': 96.0}, 
                 {u'TOT_PTS_Misc': u'Chappell, Justin', u'Total_Points': 96.0}],
                "-Total_Points,TOT_PTS_Misc")
[{u'Total_Points': 96.0, u'TOT_PTS_Misc': u'Chappell, Justin'}, 
 {u'Total_Points': 96.0, u'TOT_PTS_Misc': u'Russo, Brandon'}, 
 {u'Total_Points': 60.0, u'TOT_PTS_Misc': u'Utley, Alex'}]

解析有点脆弱,但至少它允许键之间存在可变数量的空格。

解决方案 8:

由于您已经熟悉 lambda,这里有一个不太冗长的解决方案。

>>> def itemgetter(*names):
    return lambda mapping: tuple(-mapping[name[1:]] if name.startswith('-') else mapping[name] for name in names)

>>> itemgetter('a', '-b')({'a': 1, 'b': 2})
(1, -2)

解决方案 9:

这里有一个简单的函数,可以按照帖子的要求与“任意数量的键”一起使用,并且可以通过在键名称前面添加“-”来反转任意数量的列:

b = [
    {"TOT_PTS_Misc": "Utley, Alex", "Total_Points": 96.0},
    {"TOT_PTS_Misc": "Russo, Brandon", "Total_Points": 96.0},
    {"TOT_PTS_Misc": "Chappell, Justin", "Total_Points": 96.0},
    {"TOT_PTS_Misc": "Foster, Toney", "Total_Points": 80.0},
    {"TOT_PTS_Misc": "Lawson, Roman", "Total_Points": 80.0},
    {"TOT_PTS_Misc": "Lempke, Sam", "Total_Points": 80.0},
    {"TOT_PTS_Misc": "Gnezda, Alex", "Total_Points": 78.0},
    {"TOT_PTS_Misc": "Kirks, Damien", "Total_Points": 78.0},
    {"TOT_PTS_Misc": "Worden, Tom", "Total_Points": 78.0},
    {"TOT_PTS_Misc": "Korecz, Mike", "Total_Points": 78.0},
    {"TOT_PTS_Misc": "Swartz, Brian", "Total_Points": 66.0},
    {"TOT_PTS_Misc": "Burgess, Randy", "Total_Points": 66.0},
    {"TOT_PTS_Misc": "Smugala, Ryan", "Total_Points": 66.0},
    {"TOT_PTS_Misc": "Harmon, Gary", "Total_Points": 66.0},
    {"TOT_PTS_Misc": "Blasinsky, Scott", "Total_Points": 60.0},
    {"TOT_PTS_Misc": "Carter III, Laymon", "Total_Points": 60.0},
    {"TOT_PTS_Misc": "Coleman, Johnathan", "Total_Points": 60.0},
    {"TOT_PTS_Misc": "Venditti, Nick", "Total_Points": 60.0},
    {"TOT_PTS_Misc": "Blackwell, Devon", "Total_Points": 60.0},
    {"TOT_PTS_Misc": "Kovach, Alex", "Total_Points": 60.0},
    {"TOT_PTS_Misc": "Bolden, Antonio", "Total_Points": 60.0},
    {"TOT_PTS_Misc": "Smith, Ryan", "Total_Points": 60.0},
]

def multi_key_sort(dict_list, sort_keys):
    for sort_key in reversed(sort_keys):
        dict_list = sorted(dict_list, key=lambda d: -d[sort_key[1:]] if sort_key.startswith("-") else d[sort_key])
    return dict_list

c = ["-Total_Points", "TOT_PTS_Misc"]

a = multi_key_sort(b, c)

for d in a:
    print(d)

如果您不需要对任何列进行反向排序,则更简单的函数是:

def multi_key_sort(dict_list, sort_keys):
    return sorted(dict_list, key=lambda d: tuple(d[sort_key] for sort_key in sort_keys))
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2941  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1803  
  PLM(产品生命周期管理)系统在企业的产品研发、生产与管理过程中扮演着至关重要的角色。然而,在实际运行中,资源冲突是经常会遇到的难题。资源冲突可能导致项目进度延迟、成本增加以及产品质量下降等一系列问题,严重影响企业的效益与竞争力。因此,如何有效应对PLM系统中的资源冲突,成为众多企业关注的焦点。接下来,我们将详细探讨5...
plm项目管理系统   31  
  敏捷项目管理与产品生命周期管理(PLM)的融合,正成为企业在复杂多变的市场环境中提升研发效率、增强竞争力的关键举措。随着技术的飞速发展和市场需求的快速更迭,传统的研发流程面临着诸多挑战,而将敏捷项目管理理念融入PLM,有望在2025年实现研发流程的深度优化,为企业创造更大的价值。理解敏捷项目管理与PLM的核心概念敏捷项...
plm项目   31  
  模块化设计在现代产品开发中扮演着至关重要的角色,它能够提升产品开发效率、降低成本、增强产品的可维护性与可扩展性。而产品生命周期管理(PLM)系统作为整合产品全生命周期信息的关键平台,对模块化设计有着强大的支持能力。随着技术的不断发展,到 2025 年,PLM 系统在支持模块化设计方面将有一系列令人瞩目的技术实践。数字化...
plm软件   28  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用