Python:使用自定义比较器对字典数组进行排序?

Ric*_*ard 10 python

我有以下Python字典数组:

myarr = [ { 'name': 'Richard', 'rank': 1 },
{ 'name': 'Reuben', 'rank': 4 },
{ 'name': 'Reece', 'rank': 0 },
{ 'name': 'Rohan', 'rank': 3 },
{ 'name': 'Ralph', 'rank': 2 },
{ 'name': 'Raphael', 'rank': 0 },
{ 'name': 'Robin', 'rank': 0 } ]
Run Code Online (Sandbox Code Playgroud)

我想按等级值对其进行排序,排序如下:1-2-3-4-0-0-0.

如果我尝试:

sorted_master_list = sorted(myarr, key=itemgetter('rank'))
Run Code Online (Sandbox Code Playgroud)

然后列表按0-0-0-1-2-3-4的顺序排序.

如何定义自定义比较器函数以将零推到列表底部?我想知道我是否可以使用像methodcaller这样的东西.

nin*_*cko 23

选项1:

key=lambda d:(d['rank']==0, d['rank'])
Run Code Online (Sandbox Code Playgroud)

选项2:

key=lambda d:d['rank'] if d['rank']!=0 else float('inf')
Run Code Online (Sandbox Code Playgroud)

演示:

"我想按等级值排序,按如下顺序排列:1-2-3-4-0-0-0." - 原始海报

>>> sorted([0,0,0,1,2,3,4], key=lambda x:(x==0, x))
[1, 2, 3, 4, 0, 0]

>>> sorted([0,0,0,1,2,3,4], key=lambda x:x if x!=0 else float('inf'))
[1, 2, 3, 4, 0, 0]
Run Code Online (Sandbox Code Playgroud)

 

附加评论:

"请你能解释一下(一个Python新手)它正在做什么?我可以看到它是一个lambda,我知道它是一个匿名函数:括号中的位是什么?" - OP评论

索引/切片表示法:

itemgetter('rank')lambda x: x['rank']和函数一样是一样的东西:

def getRank(myDict):
    return myDict['rank']
Run Code Online (Sandbox Code Playgroud)

[...]被称为索引/切片表示法,请参阅说明Python的切片表示法 - 另请注意,这someArray[n]是许多编程语言中用于索引的常用表示法,但可能不支持表单[start:end]或表单的切片[start:end:step].

key=vs cmp=vs富比较:

至于发生了什么,有两种常用的方法来指定排序算法的工作方式:一种是使用key函数,另一种是使用cmp函数(现在在python中已经弃用,但功能更多).虽然cmp函数允许您任意指定两个元素应该如何比较(input:a,b; output:a<ba>bor a==b).虽然合法但它没有给我们带来任何重大好处(我们必须以笨拙的方式复制代码),并且关键功能更适合您的情况.(有关如何以cmp=优雅但可能过度的方式隐式定义,请参阅"对象丰富的比较" .)

实现您的关键功能:

不幸的是0是整数的元素,因此具有自然顺序:0通常<1,2,3 ......因此,如果我们想要施加额外的规则,我们需要将列表排序在"更高级别".我们通过使键成为元组来实现这一点:元组首先按其第一个元素排序,然后按第二个元素排序.真假将永远在False之后订购,所以所有的Trues将在Falses之后订购; 然后,他们将排序为正常:(True,1)<(True,2)<(True,3)<...,(False,1)<(False,2)<...,(False,*)<(True,*).替代方案(选项2),仅为rank-0字典指定无穷大的值,因为这保证高于任何可能的等级.

更一般的替代 - 对象丰富的比较:

在更普遍的解决办法是创建表示记录的类,然后实现__lt__,__gt__,__eq__,__ne__,__gt__,__ge__,和所有其他丰富的比较操作,或者只实现其中的一个,并__eq__和使用@functools.total_ordering装饰.每当您使用比较运算符时,这将导致该类的对象使用自定义逻辑(例如x=Record(name='Joe', rank=12) y=Record(...) x<y); 由于sorted(...)函数<在比较排序中默认使用和其他比较运算符,这将使排序时自动行为,以及在其他情况下使用<和其他比较运算符.根据您的使用情况,这可能会或可能不会过多.

更清洁的替代方案 - 不要使用语义重载0:

但是,我应该指出,将0s放在1,2,3,4之后会有点人为.这是否合理取决于rank = 0是否真的意味着rank = 0; 如果rank = 0真的"低于"rank = 1(其实际上"低于"rank = 2 ......).如果确实如此,那么你的方法就完全没问题了.如果不是这种情况,那么您可以考虑省略'rank':...条目而不是设置'rank':0.然后你可以使用Lev Levitsky的答案排序'rank' in d,或者:

选项1有不同的方案:

key=lambda d: (not 'rank' in d, d['rank'])
Run Code Online (Sandbox Code Playgroud)

选项2有不同的方案:

key=lambda d: d.get('rank', float('inf'))
Run Code Online (Sandbox Code Playgroud)

旁注:依赖于python中无穷大的存在几乎是一个黑客,制作任何提到的解决方案(元组,对象比较),Lev的过滤器然后连接解决方​​案,甚至可能是稍微复杂的cmp解决方案(打字)由威尔逊),更广泛的其他语言.