【问题标题】:Sort list of dicts by two keys [duplicate]按两个键对字典列表进行排序[重复]
【发布时间】:2020-10-04 09:54:34
【问题描述】:

我有这个字典列表:

[{'score': '1.9', 'id': 756, 'factors': [1.25, 2.25, 2.5, 2.0, 1.75]}, {'score': '2.0', 'id': 686, 'factors': [2.0, 2.25, 2.75, 1.5, 2.25]}, {'score': '2.0', 'id': 55, 'factors': [1.5, 3.0, 2.5, 1.5, 1.5]}, {'score': '1.9', 'id': 863, 'factors': [1.5, 3.0, 1.5, 2.5, 1.5]}]

我可以按分数排序:sorted(l, key=lambda k: k['score'], reverse=True)。但是我已经打平了分数。如何先按分数排序,然后按 id asc 或 desc 排序?

【问题讨论】:

  • 您的意图是在比较之前将score 转换为浮点数,还是希望将其作为字符串在词法上进行比较?例如,如果两个分数是 '2.0''10.0' 怎么办?哪个会“更高”?

标签: python list python-2.7 dictionary


【解决方案1】:

为了补充而不是重复其他答案中所述的内容,这个问题提出了一个有趣的观点(尽管在问题的情况下并不直接需要),如果您想对两个键进行排序,使用 主要升序次要降序,但其中没有一个键是数字,所以你不能使用否定其中一个键的技巧 - 即键支持比较而不是否定。

假设您要对以下字典列表进行排序,主键 'name' 升序,辅助键 'surname' 降序。

l = [{'name': 'Fred', 'surname': 'Jones'}, {'name': 'Fred', 'surname': 'Roberts'},
     {'name': 'Diana', 'surname': 'Jackson'}]

可能最简单的方法是对它们进行两次排序,首先在辅助键上,利用排序是stable这一事实,因此第二次排序将保留在第一类。

l1 = sorted(l, key=lambda k:k['surname'], reverse=True)
l2 = sorted(l1, key=lambda k:k['name'])
print(l2)

或者第二个sorted 可以通过在中间列表上执行就地sort 来替换:

l1 = sorted(l, key=lambda k:k['surname'], reverse=True)
l1.sort(key=lambda k:k['name'])
print(l1)

避免必须进行两次排序的可能解决方案是定义一个比较函数cmp2,根据需要返回-1、0或1(注意辅助键的比较顺序相反),然后使用functools.cmp_to_key生成排序键:

from functools import cmp_to_key

def cmp(a, b):
    return (a > b) - (a < b)

def cmp2(k1, k2):
    return cmp(k1['name'], k2['name']) or cmp(k2['surname'], k1['surname'])

print(sorted(l, key=cmp_to_key(cmp2)))

以上两个选项都会输出:

[{'name': 'Diana', 'surname': 'Jackson'}, {'name': 'Fred', 'surname': 'Roberts'}, {'name': 'Fred', 'surname': 'Jones'}]

【讨论】:

  • 感谢 Nick(他提供了其他答案之一)对嵌套排序方法的建议。
  • 这是我们在 cmets 中讨论的一个很好的整合。
【解决方案2】:

您可以按元组排序:

sorted(l, key=lambda k: (float(k['score']), k['id']), reverse=True)

这将按score 降序排序,然后id 降序排序。注意,由于score是字符串值,需要转换成float进行比较。

[
 {'score': '2.0', 'id': 686, 'factors': [2.0, 2.25, 2.75, 1.5, 2.25]},
 {'score': '2.0', 'id': 55, 'factors': [1.5, 3.0, 2.5, 1.5, 1.5]},
 {'score': '1.9', 'id': 863, 'factors': [1.5, 3.0, 1.5, 2.5, 1.5]},
 {'score': '1.9', 'id': 756, 'factors': [1.25, 2.25, 2.5, 2.0, 1.75]}
]

要按id升序排序,请使用-k['id'](对非负数进行降序排序相当于对非负数进行升序排序):

sorted(l, key=lambda k: (float(k['score']), -k['id']), reverse=True)

[
 {'score': '2.0', 'id': 55, 'factors': [1.5, 3.0, 2.5, 1.5, 1.5]},
 {'score': '2.0', 'id': 686, 'factors': [2.0, 2.25, 2.75, 1.5, 2.25]},
 {'score': '1.9', 'id': 756, 'factors': [1.25, 2.25, 2.5, 2.0, 1.75]},
 {'score': '1.9', 'id': 863, 'factors': [1.5, 3.0, 1.5, 2.5, 1.5]}
]

【讨论】:

  • 这适用于两个键之一是数字的情况,因此可以取反。最好提供一个按一个键升序和另一个键降序排序的通用解决方案,其中两个键都支持比较但不支持否定,例如字符串。
  • 在python 2中,你可以使用sorted(l, cmp=lambda(d1, d2: (cmp(d1[k1],d2[k1]), cmp(d2[k2],d1[k2])))之类的东西,但是cmp在python 3中已经被放弃了,我想知道一个等价物。
  • 在这个特定问题的更具体的一点上,score 元素是一个字符串,但我敢打赌,预期的排序顺序是基于等效的浮点数而不是词法排序,所以你可能想要 float(k['score']) 作为 lambda 中元组的第一个元素。您需要先向 OP 寻求澄清,但如果打算让 '2.0' 大于 '10.0',我会感到惊讶。
  • @alaniwi 这是关于score 的一个好点,我没有注意到,已经更新了答案。就您的其他 cmets 而言,简单(最简单?)的解决方案是嵌套排序 sorted(sorted(l, key=lambda k: k['id']), key=lambda k: k['score'], reverse=True)
  • 我不相信嵌套排序。第二种排序将简单地按新的主键重新排序,而不是提供一个辅助键来用作决胜局。
【解决方案3】:

您可以先按第二个键对列表进行排序,然后按第一个键对结果列表进行排序。

result = sorted(sorted(l, key=lambda k: k['id']), key=lambda k: k['score'], reverse=True)

【讨论】:

    【解决方案4】:

    您可以使用自定义键做到这一点:

    d = [{'score': '1.9', 'id': 756, 'factors': [1.25, 2.25, 2.5, 2.0, 1.75]},
         {'score': '2.0', 'id': 686, 'factors': [2.0, 2.25, 2.75, 1.5, 2.25]},
         {'score': '2.0', 'id': 55, 'factors': [1.5, 3.0, 2.5, 1.5, 1.5]},
         {'score': '1.9', 'id': 863, 'factors': [1.5, 3.0, 1.5, 2.5, 1.5]}]
    
    def k(d):
        return d['score'],d['id']
    
    d = sorted(d, key=k, reverse=True)
    
    print(d)
    

    输出:

    [{'score': '2.0', 'id': 686, 'factors': [2.0, 2.25, 2.75, 1.5, 2.25]},
     {'score': '2.0', 'id': 55, 'factors': [1.5, 3.0, 2.5, 1.5, 1.5]},
     {'score': '1.9', 'id': 863, 'factors': [1.5, 3.0, 1.5, 2.5, 1.5]},
     {'score': '1.9', 'id': 756, 'factors': [1.25, 2.25, 2.5, 2.0, 1.75]}]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-11-02
      • 1970-01-01
      • 2014-02-11
      • 2016-12-11
      • 2017-02-28
      • 1970-01-01
      • 2019-12-30
      相关资源
      最近更新 更多