我有以下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 } ]
我想按等级值排序,排序如下:1-2-3-4-0-0-0。
如果我尝试:
sorted_master_list = sorted(myarr, key=itemgetter('rank'))
然后列表按0-0-0-1-2-3-4的顺序排序。
如何定义自定义比较器函数以将零推到列表底部?我想知道我是否可以使用像methodcaller这样的东西。
答案 0 :(得分:23)
选项1:
key=lambda d:(d['rank']==0, d['rank'])
选项2:
key=lambda d:d['rank'] if d['rank']!=0 else float('inf')
演示:
“我想按等级值对其进行排序,排序如下: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]
补充意见:
“请你解释一下(Python新手)它在做什么?我可以看到它是一个lambda,我知道它是一个匿名函数:括号中的位是什么?” - OP评论
索引/切片表示法:
itemgetter('rank')
与lambda x: x['rank']
相同,与函数相同:
def getRank(myDict):
return myDict['rank']
[...]
称为索引/切片表示法,请参阅Explain Python's slice notation - 另请注意,someArray[n]
是许多编程语言中用于索引的常用表示法,但可能不支持表单的切片[start:end]
或[start:end:step]
。
key=
vs cmp=
vs富裕比较:
至于发生了什么,有两种常用方法可以指定排序算法的工作方式:一种是key
函数,另一种是cmp
函数(现在不推荐使用) python,但更多功能)。虽然cmp
函数允许您任意指定两个元素应如何比较(输入:a
,b
;输出:a<b
或a>b
或{{1 }})。虽然合法但它没有给我们带来任何重大好处(我们必须以笨拙的方式复制代码),并且关键功能更适合您的情况。 (有关如何以优雅但可能过度的方式隐式定义a==b
,请参阅“对象丰富的比较”。)
实施关键功能:
不幸的是,0是整数的元素,因此具有自然顺序:0通常&lt; 1,2,3 ......因此,如果我们想要施加额外的规则,我们需要将列表排在“更高级别”。我们通过使键成为元组来实现这一点:元组首先按其第一个元素排序,然后按第二个元素排序。真假将永远在False之后订购,所以所有的Trues将在Falses之后订购;然后他们将按正常排序:cmp=
,(True,1)<(True,2)<(True,3)<...
,(False,1)<(False,2)<...
。替代方案(选项2),仅为rank-0字典指定无穷大的值,因为这保证高于任何可能的等级。
更一般的替代 - 对象丰富的比较:
更通用的解决方案是创建一个表示记录的类,然后实现(False,*)<(True,*)
,__lt__
,__gt__
,__eq__
,__ne__
,{ {1}}以及所有其他rich comparison operators,或者只是实施其中一个__gt__
并使用@functools.total_ordering
decorator。每当您使用比较运算符时,这将导致该类的对象使用自定义逻辑(例如__ge__
__eq__
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':...
或者通过:按Lev Levitsky的答案排序
选项1使用不同的方案:
'rank':0
选项2采用不同的方案:
'rank' in d
旁注:依赖于python中无穷大的存在几乎是一个黑客的边界,制作任何提到的解决方案(元组,对象比较),Lev的filter-then-concatenate solution,甚至可能稍微更多 - 复杂cmp
solution(由威尔逊打字),更适用于其他语言。
答案 1 :(得分:1)
我会做
sortedlist = sorted([x for x in myarr if x['rank']], key=lambda x: x['rank']) + [x for x in myarr if not x['rank']]
我觉得它可以以某种方式压缩。
答案 2 :(得分:1)
我更倾向于创建一个比较函数来专门处理“0”:
def compare(x,y):
if x == y:
return 0
elif x == 0:
return 1
elif y == 0:
return -1
else:
return cmp(x,y)
sorted(myarr, cmp=lambda x,y: compare(x,y), key=lambda x:x['rank'])
但是,自定义比较功能会有性能损失。
答案 3 :(得分:-1)
一种愚蠢的方法是:
sorted_master_list = sorted(myarr, key=lambda x: 99999 if x['rank'] == 0 else x['rank'])
如果你知道自己的最高等级,那么效果会很好。
答案 4 :(得分:-1)
你这里的myarr
绑定看起来不像有效的Python代码(并且不会在我的解释器会话中执行。
将其呈现为:
myarr = {
'Richard': 1,
'Reuben': 4,
'Reece': 0,
'Rohan': 3,
'Ralph': 2,
'Raphael': 0,
'Robin': 0 }
给我一些我可以基于答案的东西。
在Python中进行自定义排序的推荐方法是使用DSU(装饰,排序,未装饰)模式。 如果要按值对字典进行排序,则类似于:
keys_sorted_by_val = [ x[1] for x in sorted([(v,k) for k,v in myarr.items()])]
...其中(v,k) for k,v in myarr.items()
是装饰的表达式; sorted()
显然是排序,而外x[1] for x in ...
是最终的 undecorate 步骤。
显然,这似乎是一个足够普遍的要求,可以将其包装在一个函数中:
def dict_by_values(d):
return [ x[1] for x in sorted([(v,k) for k,v in d.items()])]
如果您有一组要按某个属性排序的对象实例,可以使用以下内容:
def sort_by_attr(attr, coll):
results = list()
for each in coll:
assert hasattr(each, attr)
results.append((getattr(each, attr), each))
results.sort()
return [x[1] for x in results]
因此,如果我们创建了一个代表你的名字/等级数据的类:
class NameRanking(object):
def __init__(self, name, rank):
self.name = name
self.rank = rank
def __repr__(self):
return "%s: %s, %s" %(self.__class__, self.name, self.rank)
...并使用myarr
实例化一个列表:
name_rankings = [姓名为k(v)为k,v为myarr.items()]
...然后我们可以使用以下方式获取该副本:
names_rankings_by_rank = sort_by_attr('rank', name_rankings)
(是的,assert
在这里不是一个好主意;那就是你将自己的异常处理或代码投入到你的应用程序中的地方。)
答案 5 :(得分:-2)
只需传递给“key”任意函数或可调用对象 -
它是需要的。 itemgetter
碰巧是一个这样的功能 - 但它可以工作
使用您编写的任何函数 - 它只需要将一个参数作为输入,然后返回
一个直接可以达到你想要的顺序的对象。
在这种情况下:
def key_func(item):
return item["rank"] if item["rank"] != 0 else -100000
sorted_master_list = sorted(myarr, key=key_func)
(它也可以写成lambda表达式)
答案 6 :(得分:-3)
您可以在密钥参数中使用函数:
用于屁股排序:
sorted_master_list = sorted(myarr, key=lambda x: x.get('rank'))
或desc:
sorted_master_list = sorted(myarr, key=lambda x: -x.get('rank'))
此外,您可以在此处阅读有关排序函数http://wiki.python.org/moin/HowTo/Sorting
答案 7 :(得分:-3)
试 sorted_master_list =已排序(myarr,key = itemgetter('rank'),reverse = True)