按值和键长度排序字典

-1 投票
3 回答
2361 浏览
提问于 2025-04-18 17:00

我一直在网上搜索,但就是找不到答案。

我有一个字典,里面存着小组里的成员和小组的分数,groups = {"john,alfred,willis":5, "james,connor":5}, ...。不过,一个人可以同时在两个小组里。我想对这个字典进行排序,按照人数和分数来排序。例如:

>>> groups = {"a,b,c":5, "d,e":6, "f,g,h,i":5, "j,k,l":6, "m,n":10, "a,d,f":5}

我需要先按分数排序,然后按人数排序,最后如果还有相同的情况,就按字母顺序来决定。虽然小组之间没有重复,但一个小组可能是 "a,b,c,d",另一个小组可能是 "a,b,c,e"。分数高的优先,人数多的优先,字母顺序就是字母顺序。

>>> print(mySort(groups))
"m,n", 10
"j,k,l", 6
"d,e", 6
"f,g,h,i", 5
"a,b,c", 5
"a,d,f", 5

输出格式不一定要那样,但最好还是像字典那样格式化。

我尝试了几种不同的方法,包括用 , 来分割名字,因为名字的长度可以不一样,但因为Python不是我的母语,我觉得有点困难。

我该如何根据值和键的大小来排序字典呢?

编辑:我在问题中添加了另一部分,原本我以为可以不提,但现在看来是需要的...

3 个回答

1

要按“人数”排序,你需要这样做:

>>> sorted(groups.items(), key=lambda p: (p[1], p[0].count(',')), reverse=True)

[('m,n', 10), ('j,k,l', 6), ('d,e', 6), ('f,g,h,i', 5), ('a,b,c', 5)]

顺便提一下,用逗号分隔的字符串并不是表示一组东西的最佳方式。可以考虑把你的字典改成用元组索引的形式:

>>> good_groups = {tuple(k.split(',')):v for k, v in groups.items()}

然后

>>> sorted(good_groups.items(), key=lambda p: (p[1], len(p[0])), reverse=True)

[(('m', 'n'), 10), (('j', 'k', 'l'), 6), (('d', 'e'), 6), (('f', 'g', 'h', 'i'), 5), (('a', 'b', 'c'), 5)]

如果你的组会被修改,并且应该是列表而不是元组,那么你就不能把它们用作字典的键。可以考虑使用其他数据结构,比如字典的列表:

groups = [
   { 'members': ['foo', 'bar'], 'score': 5 },
   { 'members': ['baz', 'spam'], 'score': 15 },
etc
3

使用 sorted:这个函数的返回值会用来进行比较。

>>> groups = {"a,b,c":5, "d,e":6, "f,g,h,i":5, "j,k,l":6, "m,n":10}
>>> sorted_keys = sorted(groups, key=lambda k: (groups[k], k), reverse=True)
>>> sorted_keys
['m,n', 'j,k,l', 'd,e', 'f,g,h,i', 'a,b,c']
>>> [(key, groups[key]) for key in sorted_keys]
[('m,n', 10), ('j,k,l', 6), ('d,e', 6), ('f,g,h,i', 5), ('a,b,c', 5)]

更新

key 函数需要按照下面的方式修改,才能正确计算人数。

lambda k: (groups[k], len(k.split(','))), reverse=True)
2
groups = {"a,b,c":5, "d,e":6, "f,g,h,i":5, "j,k,l":6, "m,n":10}
s = sorted(groups.items(),key=lambda x: (x[1],len(x[0])),reverse=True)

for k,v in s:
    print (k,v)
m,n 10
j,k,l 6
d,e 6
f,g,h,i 5
a,b,c 5

使用 -max(map(ord,x[0]))) 可以按照字母在字母表中出现的顺序进行排序,也就是说 a,b,c,y 会排在 a,b,c,z 前面。

In [37]: groups = {"a,b,c":5, "d,e":6, "f,g,h,i":5, "j,k,l":6, "m,n":10,"a,b,c,d":12,"a,b,c,e":12,"a,b,c,z":13,"a,b,c,y":13}

In [38]: sorted(groups.items(),key=lambda x: (x[1],len(x[0]),-max(map(ord,x[0].split(","))),reverse=True)
Out[38]: 
[('a,b,c,y', 13),
 ('a,b,c,z', 13),
 ('a,b,c,d', 12),
 ('a,b,c,e', 12),
 ('m,n', 10),
 ('j,k,l', 6),
 ('d,e', 6),
 ('f,g,h,i', 5),
 ('a,b,c', 5)]

我们用 lambda x: (x[1],len(x[0]),-max(map(ord,x[0].split(",")))) 来对上面的输出进行排序。

这里的 x[1],len(x[0]) 表示我们首先根据 x[1] 的值进行排序,然后再根据每个键的长度 len(x[0]) 进行排序。

如果这两者都相同,我们就会使用 -max(map(ord,x[0].split(",")) 来进一步排序,下面是一个示例来说明这个过程:

假设我们有 "a,b,c,z" 和 "a,b,c,y" 这两个例子,并把它们放在一个列表中 keys =["a,b,c,z","a,b,c,y"]

首先获取每个字符的 ASCII 值:

In [54]: ords = [list(map(ord,x.split(","))) for x in keys] # get ord values from each char
In [55]: ords
Out[55]: [[97, 98, 99, 122], [97, 98, 99, 121]]

我们是从高到低进行排序,所以使用 reverse = True

In [56]: sorted([max(x) for x in ords], reverse=True) # puts "z" first because of reversing
Out[56]: [122, 121]

因此我们使用 -max(x) 来反转这个输出:

In [57]: sorted([-max(x) for x in ords], reverse=True) # now "y" comes first
Out[57]: [-121, -122]

xlambda 中代表 groups.items() 中的每一个子项,结构如下:

([('a,b,c', 5), ('a,b,c,d', 12), ('j,k,l', 6), ('d,e', 6), ('a,b,c,z', 13), ('m,n', 10), ('a,b,c,y', 13), ('a,b,c,e', 12), ('f,g,h,i', 5)])

所以如果我们取 ('a,b,c', 5),那么 x[0] = "a,b,c"x[1] = 5

撰写回答