在现实场景中,存在多条件的排序规则,如,在一批学生中,先按照成绩降序排序,再按照出生日期降序(出生日越晚的年龄越小,越聪明),最后再按照学号升序排列。本文实现两种排序方法,以供参考。
测试数据:
test_data = [{'name': "小明", 'student_id': 16, 'score': 97, 'birthday': date_to_str('1996-10-24 18:00:00', 0)},
{'name': "小花", 'student_id': 19, 'score': 98, 'birthday': date_to_str('1996-01-24 18:00:00', 0)},
{'name': "小红", 'student_id': 18, 'score': 97, 'birthday': date_to_str('1996-12-24 18:00:00', 0)},
{'name': "小白", 'student_id': 15, 'score': 97, 'birthday': date_to_str('1996-10-24 18:00:00', 0)}]
示例排序规则:
先按照分数从高到低,再按照学号从小到大排列
score_order = OrderedDict({'score': 0, 'student_id': 1})
示例输出
方法1:
对排序条件进行循环,需要注意的是,越重要的条件越在后面循环,因此用了个列表反转,该方法可以适用所有数据类型,而且在时间表现上较方法2还要好。
def order_dict(dict_info: List[Dict], order_info: Dict):
"""
用于排序的函数,输入dict,指定排序条件,然后输出排序结果
:param dict_info: 信息字典列表
:param order_info: 排序条件,越前面的条件越重要。如{条件1:1},1表示升序(从小到大),0表示倒序(从大到小)
:return:
"""
order_ = None
flag = 1
for key, value in reversed(order_info.items()):
reverse = value == 0
if flag == 1:
order_ = sorted(dict_info, key=lambda x: x[key], reverse=reverse)
flag = 0
else:
order_ = sorted(order_, key=lambda x: x[key], reverse=reverse)
return order_
方法2:
先将条件循环形成lambda
中最终输入的语句,最后使用eval
函数执行一次性排序。但是该方法,在碰到需要倒序排序的值时,是只支持数字型的,如果碰到字符型的就会报错,除非将-
(负号)进行重载。该方法在效率上大约是方法1的两倍。那么为什么我还要将这个方法放上来呢?因为sorted
的源码是看不到的,本来以为一次性输入条件排序的速度能够更快点,结果还更慢,猜测sorted
的lambda
在处理多条件的时候,也是用方法1中倒叙循环的方法。
def order_dict_two(dict_info: List[Dict], order_info: Dict):
"""
用于排序的函数,输入dict,指定排序条件,然后输出排序结果
:param dict_info: 信息字典列表
:param order_info: 排序条件,越前面的条件越重要。如{条件1:1},1表示升序(从小到大),0表示倒序(从大到小)
:return:
"""
order_lambda = []
for key, value in order_info.items():
if value:
order_lambda.append('x["%s"]' % key)
else:
order_lambda.append('-x["%s"]' % key)
exc = 'sorted(dict_info, key=lambda x: (%s))' % (','.join(order_lambda))
order_ = eval(exc)
return order_
参考资料