Python按多个键排序字典列表

sim*_*imi 81 python

我有一个dicts列表:

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}]
Run Code Online (Sandbox Code Playgroud)

我需要使用Total_Points反转的多键排序,然后不要反转TOT_PTS_Misc.

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

a = sorted(b, key=lambda d: (-d['Total_Points'], d['TOT_PTS_Misc']))
Run Code Online (Sandbox Code Playgroud)

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

如何使用lambda线对列表进行排序,对于传递给multikeysort函数的任意数量的键,并考虑sortkeys可以有任意数量的键,并且需要反向排序的那些键.前面有' - '吗?

hug*_*own 66

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

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)
Run Code Online (Sandbox Code Playgroud)

你可以这样称呼它:

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
Run Code Online (Sandbox Code Playgroud)

尝试使用任一列否定.您将看到排序顺序相反.

下一步:更改它,以便它不使用额外的类....


2016年1月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)
Run Code Online (Sandbox Code Playgroud)

万一你喜欢你的代码简洁.


2016-01-17之后

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

from operator import itemgetter as i
from functools import cmp_to_key

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))
Run Code Online (Sandbox Code Playgroud)

灵感来自这个答案我应该如何在Python 3中进行自定义排序?

  • @hughdbrown:你删除了`cmp`关键字,但`cmp()`函数仍然使用了4行以上.我尝试使用3.2,3.3,3.4和3.5,它们都在函数调用时失败了,因为`cmp()`没有定义.这里的第三个项目(https://docs.python.org/3.0/whatsnew/3.0.html#ordering-comparisons)提到将`cmp()`视为已消失. (7认同)
  • `cmp()`不适用于Python3,所以我必须自己定义它,如下所述:http://stackoverflow.com/a/22490617/398514 (4认同)
  • 谢谢,你救了我的一天! (2认同)

Sco*_*ord 43

本文对各种技术进行了很好的概述.如果您的要求比"完全双向多键"更简单,请查看.很明显接受的答案和我刚引用的博客文章在某种程度上相互影响,虽然我不知道哪个顺序.

如果链接在此处死亡,则上面未提及的示例的快速概要如下:

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']))
Run Code Online (Sandbox Code Playgroud)

  • 感谢大纲,Link现在已经死了.:) (4认同)
  • 提供的链接中没有详细的解释,只是一个已经显而易见的一般想法。python 官方网站上有更好的解释:https://docs.python.org/3/howto/sorting.html (2认同)

wou*_*lee 36

我知道这是一个相当古老的问题,但没有一个答案提到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'))
Run Code Online (Sandbox Code Playgroud)

倒车/倒车适用于所有可订购类型,而不仅仅是您可以通过在前面加上减号来抵消的数字.

而且由于(至少)CPython中使用的Timsort算法,实际上这实际上相当快.

  • 有趣的是,你没有注意到主要的排序标准是最后的,如我的例子中所示,并在另一条评论中明确提到,以便在你没有注意到的情况下非常清楚. (5认同)
  • 非常好.对于中等数据集,多次对集合进行排序并不重要,这非常酷!正如您所指出的,与sql排序相比,您必须反转python排序.谢谢. (2认同)

Ale*_*lli 24

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']))
Run Code Online (Sandbox Code Playgroud)

  • 但是如果你否定字符串值而不是数字值呢?我不认为那会奏效。 (2认同)

vol*_*ano 10

我今天遇到了类似的问题 - 我必须通过降序数值和升序字符串值对字典项进行排序。为了解决方向冲突的问题,我否定了整数值。

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

sorted(b, key=lambda e: (-e['Total_Points'], e['TOT_PTS_Misc']))
Run Code Online (Sandbox Code Playgroud)

非常简单 - 就像一个魅力

[{'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}]
Run Code Online (Sandbox Code Playgroud)


小智 5

我使用以下内容在多个列上排序2d数组

def k(a,b):
    def _k(item):
        return (item[a],item[b])
    return _k
Run Code Online (Sandbox Code Playgroud)

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

>>> 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]]
Run Code Online (Sandbox Code Playgroud)