duplicate_array= [
{'id': 1, 'name': 'john', 'count': 1},
{'id': 1, 'name': 'john', 'count': 2},
{'id': 2, 'name': 'peter', 'count': 1},
]
如何获得唯一字典的列表,在累积重复字典的"计数"的同时删除重复字典?
[
{'id': 1, 'name': 'john', 'count': 3}, //here is main use case that I want to get total count
{'id': 2, 'name': 'peter', 'count': 1},
]
我试过这得到唯一的值,但我不知道如何积累的结果?
final = list({v['id']:v for v in duplicate_array}.values())
下面是一些不使用任何python库的代码。但是这会导致代码变长。
duplicate_array= [
{'id': 1, 'name': 'john', 'count': 1},
{'id': 1, 'name': 'john', 'count': 2},
{'id': 2, 'name': 'peter', 'count': 1},
]
final=[]
for i, x in enumerate(duplicate_array):
count = 0
for d in duplicate_array.copy():
if d != 0 and d["id"] == x["id"] and d["name"] == x["name"]:
count += d["count"]
duplicate_array.remove(d)
duplicate_array.insert(i, 0)
x["count"] = count
final.append(x)
在第一个代码块中,定义原始列表并初始化输出列表。
然后是for循环
首先,我们将count初始化为0。然后再次循环遍历列表,查找与当前字典具有相同id和名称的所有字典。如果存在,则将count与它们的count值相加,并将它们从列表中删除。我们还检查字典是否为非零,因为稍后要向数组中添加零。这可以防止程序崩溃。
我们在列表的当前位置插入一个0,以防止python跳过下一项。For循环保存一个计数器,用于记录它们在python中所处的项目。然而,当我们删除当前项时(我们在嵌套的for循环中做了),这个计数器将不再匹配正确的项,因为所有next项都向左移动了一位。通过在原始列表中插入一个零,我们将所有项移回,并使索引再次正确。
最后,将原始字典的计数设置为刚才计算的值,并将唯一字典添加到最终列表中。
在这段代码之后,duplicate_array
将被填满0。如果你不想这样做,你可以先用duplicate_array.copy()
复制列表。
因此,如果您可以使用Pandas,则可以这样做:
import pandas as pd
results = pd.DataFrame(duplicate_array).groupby(["id", "name"]).agg("sum").reset_index().to_dict(orient="records")
缺点是你正在使用一个相当大的库,但我认为这种方式的可读性很好。
最好将其封装在自己的函数中:
def dedupe(dt: list) -> list:
dx = dict()
for item in dt:
key_id = (item.get('id'), item.get('name')) # We assume that an id+name is a unique identity
current = dx.get(key_id, {
'id': item.get('id'),
'name': item.get('name'),
} # get() lets us provide a default value if it doesn't exist yet
current['count'] = current.get('count', 0) + item.get('count', 0) # update the current count with the count from the new item.
dx[key_id] = current # Update the result dictionary
return [d for _, d in dx] # Convert back to a list
duplicate_array = [
{'id': 1, 'name': 'john', 'count': 1},
{'id': 1, 'name': 'john', 'count': 2},
{'id': 2, 'name': 'peter', 'count': 1},
]
result = dedupe(duplicate_array)
这利用了几个常见的python特性:
- 可哈希的元组可以用作字典中的键。
- 我们可以使用
get
来提供一个默认值,在这种情况下是一个"初始化"值。当我们第一次看到一个唯一键(它不存在于字典中)时,我们提供这个值。然后我们从复制的数组中添加计数。 - 因为我们使用字典来累积结果,所以我们可以利用唯一键来删除数组的重复项。只需要end是将字典中的值作为新数组。
注意,key_id
可以是字典的id
,而不是id和名称的组合。这应该在O(2n)
或O(n)
中完成。您只遍历一次初始列表,然后遍历一次结果列表(如果存在重复列表,则结果列表会更小)。如果您愿意使用字典而不是列表,则可以跳过第二步。
另一种方法是分两步,首先获得所有唯一的id,然后累积这些id的所有计数:
st = {(item.get('id'), item.get('name')) for item in duplicate_array}
ls = [{'id': id, 'name': name, 'count': sum(item.get('count') for item in duplicate_array if item.get('id') == id)} for id, name in st]
输出结果:
>>> st = {(item.get('id'), item.get('name')) for item in duplicate_array}
>>> ls = [{'id': id, 'name': name, 'count': sum(item.get('count') for item in duplicate_array if item.get('id') == id)} for id, name in st]
>>> ls
[{'id': 2, 'name': 'peter', 'count': 1}, {'id': 1, 'name': 'john', 'count': 3}]
这更紧凑,但更难解压缩。第一个传递(st = ...
)是创建一组元组,类似于第一个选项。第二步是创建一个字典数组,其中每个字典遍历原始数组,查找应该累加到count中的值。
我确实认为这将在非常大的集合中变慢,因为ls =...
中的每个新字典创建都会经过整个数组。在最坏的情况下,如果没有副本,这意味着O(n^2)
。但如果你想要的是紧凑性,那就对了。