我有一个字典列表。我希望每个唯一的 api 只有一个结果,结果需要根据优先级显示:0、1、2。 我可以知道我应该怎么做吗?
数据:
[
{'api':'test1', 'result': 0},
{'api':'test2', 'result': 1},
{'api':'test3', 'result': 2},
{'api':'test3', 'result': 0},
{'api':'test3', 'result': 1},
]
预期产出:
[
{'api':'test1', 'result': 0},
{'api':'test2', 'result': 1},
{'api':'test3', 'result': 0},
]
假设输入data
你可以做经典的sql-ishgroupby
:
from itertools import groupby
# in case your data is sorted already by api skip the below line
data = sorted(data, key=lambda x: x['api'])
res = [
{'api': g, 'result': min(v, key=lambda x: x['result'])['result']}
for g, v in groupby(data, lambda x: x['api'])
]
输出:
[{'api': 'test1', 'result': 0}, {'api': 'test2', 'result': 1}, {'api': 'test3', 'result': 0}]
您可以浏览一次列表,并保留每个组看到的最佳列表。 这是节省时间和空间的。
def get_min_unique(items, id_key, value_key):
lowest = {}
for item in items:
key = item[id_key]
if key not in lowest or lowest[key][value_key] > item[value_key]:
lowest[key] = item
return list(lowest.values())
例如,使用您自己的数据:
data = [
{'api':'test1', 'result': 0},
{'api':'test2', 'result': 1},
{'api':'test3', 'result': 2},
{'api':'test3', 'result': 0},
{'api':'test3', 'result': 1},
]
assert get_min_unique(data, 'api', 'result') == [
{'api': 'test1', 'result': 0},
{'api': 'test2', 'result': 1},
{'api': 'test3', 'result': 0},
]
data = [
{'api': 'test1', 'result': 0},
{'api': 'test3', 'result': 2},
{'api': 'test2', 'result': 1},
{'api': 'test3', 'result': 1},
{'api': 'test3', 'result': 0}
]
def find(data):
step1 = sorted(data, key=lambda k: k['result'])
print('step1', step1)
step2 = {}
for each in step1:
if each['api'] not in step2:
step2[each['api']] = each
print('step2', step2)
step3 = list(step2.values())
print('step3', step3)
print('n')
return step3
find(data)
试试这个,它会给你
step1 [{'api': 'test1', 'result': 0}, {'api': 'test3', 'result': 0}, {'api': 'test2', 'result': 1}, {'api': 'test3', 'result': 1}, {'api': 'test3', 'result': 2}]
step2 {'test1': {'api': 'test1', 'result': 0}, 'test3': {'api': 'test3', 'result': 0}, 'test2': {'api': 'test2', 'result': 1}}
step3 [{'api': 'test1', 'result': 0}, {'api': 'test3', 'result': 0}, {'api': 'test2', 'result': 1}]
首先对所有内容进行排序,然后首先找到每个"api",然后得到您的结果。
沉迷于代码高尔夫:
from itertools import groupby
dut = [
{'api':'test1', 'result': 0},
{'api':'test2', 'result': 1},
{'api':'test3', 'result': 2},
{'api':'test3', 'result': 0},
{'api':'test3', 'result': 1},
]
res = [
next(g)
for _,g in groupby(
sorted(dut, key=lambda d: tuple(d.values())),
key=lambda i: i['api']
)
]
结果:
Out[45]:
[{'api': 'test1', 'result': 0},
{'api': 'test2', 'result': 1},
{'api': 'test3', 'result': 0}]
使用itertools.groupby 实用程序,作为第一个参数的可迭代对象按升序排序,使用sorted
按api
和result
排序,并仅按result
分组。
groupby
返回键的可迭代对象,以及此组中项的可迭代对象,如下所示:
In [56]: list(groupby(sorted(dut, key=lambda i: tuple(i.values())), key=lambda i: i['api']))
Out[56]:
[('test1', <itertools._grouper at 0x10af4c550>),
('test2', <itertools._grouper at 0x10af4c400>),
('test3', <itertools._grouper at 0x10af4cc88>)]
使用列表推导式,由于组已经排序,因此next
用于获取组中的第一项,并且组键被丢弃。
如果您需要在每个优先级上存储每个 api,并且只定期将其过滤到最高优先级,则现有答案很好。但是,如果您只需要每个 API 的最高优先级,我认为您使用了错误的数据结构。
>>> from collections import UserDict
>>>
>>> class DataContainer(UserDict):
... def __setitem__(self, key, value):
... cur = self.get(key)
... if cur is None or value < cur:
... super().__setitem__(key, value)
... def __str__(self):
... return 'n'.join(("'api': {}, 'result': {}".format(k, v) for k, v in self.items()))
...
>>> data = DataContainer()
>>> data['test1'] = 0
>>> data['test2'] = 1
>>> data['test3'] = 2
>>> data['test3'] = 0
>>> data['test3'] = 1
>>> print(data)
'api': test1, 'result': 0
'api': test2, 'result': 1
'api': test3, 'result': 0
此容器将仅包含每个 API 的最高优先级。优点包括:
- 清楚地表达你在做什么
- 无需代码高尔夫
- 将内存占用降至最低
- 比定期排序、分组和筛选更快
不像其他解决方案那样干净,但我认为一步一步,易于理解
l = [
{'api':'test1', 'result': 0},
{'api':'test2', 'result': 1},
{'api':'test3', 'result': 2},
{'api':'test3', 'result': 0},
{'api':'test3', 'result': 1},
]
j = {'api':[], 'result':[]}
for i in l:
if i['api'] not in j['api']:
j['api'].append(i['api'])
j['result'].append(i['result'])
else:
index = j['api'].index(i['api'])
if j['result'][index]>i['result']:
j['result'][index] = i['result']
result = []
for i in range(len(j['api'])):
result.append({'api':j['api'][i],'result':j['result'][i]})
print(result)
输出
[{'api': 'test1', 'result': 0},
{'api': 'test2', 'result': 1},
{'api': 'test3', 'result': 0}]
您可以选择另一种更有效的数据结构:计数器字典。
您可以保留每个 API 的结果分布,并且代码相对简单:
data = [
{'api':'test1', 'result': 0},
{'api':'test2', 'result': 1},
{'api':'test3', 'result': 2},
{'api':'test3', 'result': 0},
{'api':'test3', 'result': 1},
]
from collections import Counter
results = {}
for d in data:
counter = results.setdefault(d['api'], Counter())
counter[d['result']] += 1
results
# {'test1': Counter({0: 1}),
# 'test2': Counter({1: 1}),
# 'test3': Counter({2: 1, 0: 1, 1: 1})}
[{'api': api, 'result':min(v.keys())} for api, v in results.items()]
# [{'api': 'test1', 'result': 0},
# {'api': 'test2', 'result': 1},
# {'api': 'test3', 'result': 0}]
如果您想获得结果的最大值或计数,则只需更改最后一行。
这是最干净的解决方案(如果您愿意使用外部库):
import pandas as pd
df = pd.DataFrame(data)
dfMin = df.groupby(by='api').min()
dfMin
是一个 Pandas 数据帧,其索引api
,并result
每个 API 的最小值。
另一个解决方案..
result = {}
for d in data: result[ d['api']] = min(result.get(d['api'], d['result']), d['result'])
new_data = [ {'api' : k, 'result': v} for k, v in result.items() ]
print (new_data)
指纹
#[{'api': 'test1', 'result': 0}, {'api': 'test2', 'result': 1}, {'api': 'test3', 'result': 0}]