我有一个dicts列表,我想删除具有相同键和值对的dicts。
对于此列表:[{'a': 123}, {'b': 123}, {'a': 123}]
我想退一步:[{'a': 123}, {'b': 123}]
另一个例子:
对于此列表:[{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}, {'a': 123, 'b': 1234}]
我想退一步:[{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}]
答案 0 :(得分:172)
试试这个:
[dict(t) for t in {tuple(d.items()) for d in l}]
策略是将字典列表转换为元组列表,其中元组包含字典的项目。由于可以对元组进行哈希处理,因此可以使用set
删除重复项(在此处使用 set comprehension ,较旧的python替代将为set(tuple(d.items()) for d in l)
),然后重新创建来自dict
的元组的词典。
其中:
l
是原始列表d
是列表中的词典之一t
是从字典编辑:如果您想保留排序,上面的单行将无效,因为set
不会这样做。但是,只需几行代码,您也可以这样做:
l = [{'a': 123, 'b': 1234},
{'a': 3222, 'b': 1234},
{'a': 123, 'b': 1234}]
seen = set()
new_l = []
for d in l:
t = tuple(d.items())
if t not in seen:
seen.add(t)
new_l.append(d)
print new_l
示例输出:
[{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}]
注意:正如@alexis指出的那样,两个具有相同键和值的字典可能不会产生相同的元组。如果他们通过不同的添加/删除密钥历史记录,则可能发生这种情况如果您的问题就是这种情况,那么请考虑按照他的建议对d.items()
进行排序。
答案 1 :(得分:37)
基于列表理解的另一个单行程序:
>>> d = [{'a': 123}, {'b': 123}, {'a': 123}]
>>> [i for n, i in enumerate(d) if i not in d[n + 1:]]
[{'b': 123}, {'a': 123}]
由于我们可以使用dict
比较,我们只保留不在初始列表的其余部分的元素(此概念只能通过索引n
访问,因此使用{ {1}})。
答案 2 :(得分:13)
有时旧式循环仍然有用。这段代码比jcollado的长一点,但很容易阅读:
a = [{'a': 123}, {'b': 123}, {'a': 123}]
b = []
for i in range(0, len(a)):
if a[i] not in a[i+1:]:
b.append(a[i])
答案 3 :(得分:13)
如果您对嵌套字典(如反序列化的JSON对象)进行操作,则其他答案将无效。对于这种情况,您可以使用:
import json
set_of_jsons = {json.dumps(d, sort_keys=True) for d in X}
X = [json.loads(t) for t in set_of_jsons]
答案 4 :(得分:9)
如果您想保留订单,那么您可以
from collections import OrderedDict
print OrderedDict((frozenset(item.items()),item) for item in data).values()
# [{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}]
如果订单无关紧要,那么你可以
print {frozenset(item.items()):item for item in data}.values()
# [{'a': 3222, 'b': 1234}, {'a': 123, 'b': 1234}]
答案 5 :(得分:4)
如果可以使用第三方软件包,则可以使用iteration_utilities.unique_everseen
:
>>> from iteration_utilities import unique_everseen
>>> l = [{'a': 123}, {'b': 123}, {'a': 123}]
>>> list(unique_everseen(l))
[{'a': 123}, {'b': 123}]
它保留了原始列表的顺序,并且ut还可以通过使用较慢的算法(O(n*m)
,其中n
是原始列表中的元素,而{{1 }}原始列表中的唯一元素,而不是m
)。如果键和值都是可哈希的,则可以使用该函数的O(n)
参数为“唯一性测试”创建可哈希的项(以便它在key
中起作用)。
对于字典(其比较与顺序无关),您需要将其映射到另一个类似的数据结构,例如O(n)
:
frozenset
请注意,您不应该使用简单的>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]
方法(不进行排序),因为相等的字典不一定具有相同的顺序(即使在Python 3.7中,插入顺序-不是绝对订单-得到保证):
tuple
如果键不可排序,甚至对元组进行排序也可能不起作用:
>>> d1 = {1: 1, 9: 9}
>>> d2 = {9: 9, 1: 1}
>>> d1 == d2
True
>>> tuple(d1.items()) == tuple(d2.items())
False
我认为比较这些方法的性能可能会很有用,所以我做了一个小型基准测试。基准图是时间与列表大小的比较,该图表基于不包含重复项的列表(可以随意选择,如果我添加一些或大量重复项,运行时不会发生明显变化)。这是一个对数-对数图,因此涵盖了整个范围。
绝对时间:
相对于最快方法的时机:
thefourtheye中的第二种方法在这里最快。使用>>> d3 = {1: 1, 'a': 'a'}
>>> tuple(sorted(d3.items()))
TypeError: '<' not supported between instances of 'str' and 'int'
函数的unique_everseen
方法排在第二位,但这是保留顺序的最快方法。 jcollado和thefourtheye的其他方法几乎一样快。对于更长的列表,使用没有密钥的key
的方法以及Emmanuel和Scorpil的解决方案速度很慢,并且对于unique_everseen
而不是O(n*n)
而言,其表现要差得多。 stpk使用O(n)
的方法不是json
,但比类似的O(n*n)
方法要慢得多。
用于重现基准的代码:
O(n)
为完整起见,以下是仅包含重复项的列表的时间:
from simple_benchmark import benchmark
import json
from collections import OrderedDict
from iteration_utilities import unique_everseen
def jcollado_1(l):
return [dict(t) for t in {tuple(d.items()) for d in l}]
def jcollado_2(l):
seen = set()
new_l = []
for d in l:
t = tuple(d.items())
if t not in seen:
seen.add(t)
new_l.append(d)
return new_l
def Emmanuel(d):
return [i for n, i in enumerate(d) if i not in d[n + 1:]]
def Scorpil(a):
b = []
for i in range(0, len(a)):
if a[i] not in a[i+1:]:
b.append(a[i])
def stpk(X):
set_of_jsons = {json.dumps(d, sort_keys=True) for d in X}
return [json.loads(t) for t in set_of_jsons]
def thefourtheye_1(data):
return OrderedDict((frozenset(item.items()),item) for item in data).values()
def thefourtheye_2(data):
return {frozenset(item.items()):item for item in data}.values()
def iu_1(l):
return list(unique_everseen(l))
def iu_2(l):
return list(unique_everseen(l, key=lambda inner_dict: frozenset(inner_dict.items())))
funcs = (jcollado_1, Emmanuel, stpk, Scorpil, thefourtheye_1, thefourtheye_2, iu_1, jcollado_2, iu_2)
arguments = {2**i: [{'a': j} for j in range(2**i)] for i in range(2, 12)}
b = benchmark(funcs, arguments, 'list size')
%matplotlib widget
import matplotlib as mpl
import matplotlib.pyplot as plt
plt.style.use('ggplot')
mpl.rcParams['figure.figsize'] = '8, 6'
b.plot(relative_to=thefourtheye_2)
除了没有# this is the only change for the benchmark
arguments = {2**i: [{'a': 1} for j in range(2**i)] for i in range(2, 12)}
功能的unique_everseen
以外,时间没有明显变化,在这种情况下,这是最快的解决方案。但是,这是具有不可散列值的函数的最佳情况(因此不具有代表性),因为它的运行时取决于列表中唯一值的数量:key
在这种情况下仅为1,因此它在{{ 1}}。
免责声明:我是O(n*m)
的作者。
答案 6 :(得分:4)
如果您在工作流程中使用熊猫,一种选择是直接将字典列表提供给pd.DataFrame
构造函数。然后使用drop_duplicates
和to_dict
方法获得所需的结果。
import pandas as pd
d = [{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}, {'a': 123, 'b': 1234}]
d_unique = pd.DataFrame(d).drop_duplicates().to_dict('records')
print(d_unique)
[{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}]
答案 7 :(得分:1)
不是通用答案,但是如果您的列表恰好被某个键排序,请执行以下操作:
l=[{'a': {'b': 31}, 't': 1},
{'a': {'b': 31}, 't': 1},
{'a': {'b': 145}, 't': 2},
{'a': {'b': 25231}, 't': 2},
{'a': {'b': 25231}, 't': 2},
{'a': {'b': 25231}, 't': 2},
{'a': {'b': 112}, 't': 3}]
然后解决方案就像:
import itertools
result = [a[0] for a in itertools.groupby(l)]
结果:
[{'a': {'b': 31}, 't': 1},
{'a': {'b': 145}, 't': 2},
{'a': {'b': 25231}, 't': 2},
{'a': {'b': 112}, 't': 3}]
使用嵌套字典并(显然)保留顺序。
答案 8 :(得分:1)
最简单的方法,将列表中的每个项目转换为字符串,因为字典不可哈希。然后你可以使用 set 删除重复项。
list_org = [{'a': 123}, {'b': 123}, {'a': 123}]
list_org_updated = [ str(item) for item in list_org]
print(list_org_updated)
["{'a': 123}", "{'b': 123}", "{'a': 123}"]
unique_set = set(list_org_updated)
print(unique_set)
{"{'b': 123}", "{'a': 123}"}
您可以使用该集合,但如果您确实想要一个列表,请添加以下内容:
import ast
unique_list = [ast.literal_eval(item) for item in unique_set]
print(unique_list)
[{'b': 123}, {'a': 123}]
答案 9 :(得分:0)
您可以使用一组,但需要将dicts转换为可哈希类型。
seq = [{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}, {'a': 123, 'b': 1234}]
unique = set()
for d in seq:
t = tuple(d.iteritems())
unique.add(t)
独特现在等于
set([(('a', 3222), ('b', 1234)), (('a', 123), ('b', 1234))])
要获得决定:
[dict(x) for x in unique]
答案 10 :(得分:0)
不太短,但易于阅读:
list_of_data = [{'a': 123}, {'b': 123}, {'a': 123}]
list_of_data_uniq = []
for dict in list_of_data:
if dict not in list_of_data_uniq:
list_of_data_uniq.append(dict)
现在列表“ list_of_data_uniq”将具有uniq字典。
答案 11 :(得分:-1)
我知道它可能不如其他答案那么优雅,但是尝试一下:
arts = list of dicts
arts_alt = []
arts_alt = [arts_alt.append(art) for art in arts if art not in arts_alt]
您需要的是arts_alt
答案 12 :(得分:-1)
很多搜索重复值和键的好例子,下面是我们过滤列表中整个字典重复数据的方法。如果源数据由 EXACT 格式的字典组成并查找重复项,请使用 dupKeys = []。否则将 dupKeys = 设置为您不想重复输入的数据的键名,可以是 1 到 n 个键。它不优雅,但有效且非常灵活
import binascii
collected_sensor_data = [{"sensor_id":"nw-180","data":"XXXXXXX"},
{"sensor_id":"nw-163","data":"ZYZYZYY"},
{"sensor_id":"nw-180","data":"XXXXXXX"},
{"sensor_id":"nw-97", "data":"QQQQQZZ"}]
dupKeys = ["sensor_id", "data"]
def RemoveDuplicateDictData(collected_sensor_data, dupKeys):
checkCRCs = []
final_sensor_data = []
if dupKeys == []:
for sensor_read in collected_sensor_data:
ck1 = binascii.crc32(str(sensor_read).encode('utf8'))
if not ck1 in checkCRCs:
final_sensor_data.append(sensor_read)
checkCRCs.append(ck1)
else:
for sensor_read in collected_sensor_data:
tmp = ""
for k in dupKeys:
tmp += str(sensor_read[k])
ck1 = binascii.crc32(tmp.encode('utf8'))
if not ck1 in checkCRCs:
final_sensor_data.append(sensor_read)
checkCRCs.append(ck1)
return final_sensor_data
final_sensor_data = [{"sensor_id":"nw-180","data":"XXXXXXX"},
{"sensor_id":"nw-163","data":"ZYZYZYY"},
{"sensor_id":"nw-97", "data":"QQQQQZZ"}]
答案 13 :(得分:-2)
这是一个快速的单行解决方案,具有双重嵌套的列表理解功能(基于@Emmanuel的解决方案)。
这将每个字典中的单个键(例如a
)用作主键,而不是检查整个字典是否匹配
[i for n, i in enumerate(list_of_dicts) if i.get(primary_key) not in [y.get(primary_key) for y in list_of_dicts[n + 1:]]]
这不是OP所要的,但这是使我进入此线程的原因,所以我认为我应该发布最终得到的解决方案