如何从列表中检索最小唯一值?

  • 本文关键字:唯一 检索 列表 python
  • 更新时间 :
  • 英文 :


我有一个字典列表。我希望每个唯一的 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 实用程序,作为第一个参数的可迭代对象按升序排序,使用sortedapiresult排序,并仅按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}]

最新更新