itertools.groupby实现分组排序
最近工作中遇到一个组内排序的问题,之前类似问题都是使用sql来解决,(比如row_number() over(partition by 分组字段 order by 排序字段 desc))。但是使用代码却是第一次,发现功能强大的itertools模块中的groupby方法可以完美解决此类需求。
当前场景:经过一系列处理后,得到如下列表
li = [{'number': 'CN111', 'title': 'title1', 'date': 20080801},
{'number': 'CN333', 'title': 'title2', 'date': 20220116},
{'number': 'CN555', 'title': 'title3', 'date': 20010606},
{'number': 'CN111', 'title': 'title4', 'date': 20090901},
{'number': 'CN555', 'title': 'title5', 'date': 20030906},
{'number': 'CN444', 'title': 'title6', 'date': 20190429},
{'number': 'CN555', 'title': 'title7', 'date': 20201211},
{'number': 'CN222', 'title': 'title8', 'date': 20141112},
{'number': 'CN111', 'title': 'title9', 'date': 20000201},
{'number': 'CN555', 'title': 'title10', 'date': 20211230},
{'number': 'CN333', 'title': 'title11', 'date': 20070601}
]
提炼后的需求:过滤出每个最新的number信息,例如
{'number': 'CN111', 'title': 'title4', 'date': 20090901}
{'number': 'CN333', 'title': 'title2', 'date': 20220116}
思路:
1.首先对li内所有字典根据分组字段,即number进行排序,目的是将number相同的字典元素排到一起,这一步是必要的,否则后续groupby没法得到正确结果
2.使用groupby进行分组
对groupby简单介绍:
groupby就是对可迭代对象的批量操作,作用就是把可迭代对象中相邻的重复元素挑出来放一起,这也是第一步需要先进行排序的原因
Code:
li.sort(key=itemgetter('number')) # itemgetter可快速实现对字典列表根据某个指定key进行排序的需求
for key, data in groupby(li, key=itemgetter('number')):
print(key)
print(data)
print(list(data))
Output:
CN111
<itertools._grouper object at 0x000002059D1D73A0>
[{'number': 'CN111', 'title': 'title1', 'date': 20080801}, {'number': 'CN111', 'title': 'title4', 'date': 20090901}, {'number': 'CN111', 'title': 'title9', 'date': 20000201}]
CN222
<itertools._grouper object at 0x000002059D1D7640>
[{'number': 'CN222', 'title': 'title8', 'date': 20141112}]
CN333
<itertools._grouper object at 0x000002059D1D73A0>
[{'number': 'CN333', 'title': 'title2', 'date': 20220116}, {'number': 'CN333', 'title': 'title11', 'date': 20070601}]
CN444
<itertools._grouper object at 0x000002059D1D7640>
[{'number': 'CN444', 'title': 'title6', 'date': 20190429}]
CN555
<itertools._grouper object at 0x000002059D1D73A0>
[{'number': 'CN555', 'title': 'title3', 'date': 20010606}, {'number': 'CN555', 'title': 'title5', 'date': 20030906}, {'number': 'CN555', 'title': 'title7', 'date': 20201211}, {'number': 'CN555', 'title': 'title10', 'date': 20211230}]
不难看出,key为分组字段,data是一个itertools._grouper类型的迭代对象,list(data)表示的是将该迭代对象转化为列表,后续操作都是对list(data)进行操作。
最终代码:
from itertools import groupby
from operator import itemgetter
li = [{'number': 'CN111', 'title': 'title1', 'date': 20080801},
{'number': 'CN333', 'title': 'title2', 'date': 20220116},
{'number': 'CN555', 'title': 'title3', 'date': 20010606},
{'number': 'CN111', 'title': 'title4', 'date': 20090901},
{'number': 'CN555', 'title': 'title5', 'date': 20030906},
{'number': 'CN444', 'title': 'title6', 'date': 20190429},
{'number': 'CN555', 'title': 'title7', 'date': 20201211},
{'number': 'CN222', 'title': 'title8', 'date': 20141112},
{'number': 'CN111', 'title': 'title9', 'date': 20000201},
{'number': 'CN555', 'title': 'title10', 'date': 20211230},
{'number': 'CN333', 'title': 'title11', 'date': 20070601}
]
li.sort(key=itemgetter('number')) # itemgetter可快速实现对字典列表的根据某个指定key进行排序的需求
for key, data in groupby(li, key=itemgetter('number')):
tmp = list(data)
tmp.sort(key=itemgetter('date'), reverse=True)
print(tmp)
print(tmp[0])
print('='*500)
Output:
[{'number': 'CN111', 'title': 'title4', 'date': 20090901}, {'number': 'CN111', 'title': 'title1', 'date': 20080801}, {'number': 'CN111', 'title': 'title9', 'date': 20000201}]
{'number': 'CN111', 'title': 'title4', 'date': 20090901}
====================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================
[{'number': 'CN222', 'title': 'title8', 'date': 20141112}]
{'number': 'CN222', 'title': 'title8', 'date': 20141112}
====================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================
[{'number': 'CN333', 'title': 'title2', 'date': 20220116}, {'number': 'CN333', 'title': 'title11', 'date': 20070601}]
{'number': 'CN333', 'title': 'title2', 'date': 20220116}
====================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================
[{'number': 'CN444', 'title': 'title6', 'date': 20190429}]
{'number': 'CN444', 'title': 'title6', 'date': 20190429}
====================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================
[{'number': 'CN555', 'title': 'title10', 'date': 20211230}, {'number': 'CN555', 'title': 'title7', 'date': 20201211}, {'number': 'CN555', 'title': 'title5', 'date': 20030906}, {'number': 'CN555', 'title': 'title3', 'date': 20010606}]
{'number': 'CN555', 'title': 'title10', 'date': 20211230}
如上便取到了每个number最新的记录