Bre*_*den 113 python dictionary list
我有一个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}]
jco*_*ado 197
试试这个:
[dict(t) for t in {tuple(d.items()) for d in l}]
Run Code Online (Sandbox Code Playgroud)
策略是将字典列表转换为元组列表,其中元组包含字典的项目.由于元组可以被散列,你可以使用set
(在这里使用集合理解,更旧的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
Run Code Online (Sandbox Code Playgroud)
示例输出:
[{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}]
Run Code Online (Sandbox Code Playgroud)
注意:正如@alexis指出的那样,两个具有相同键和值的字典可能不会产生相同的元组.如果他们通过不同的添加/删除密钥历史记录,则可能发生这种情况 如果您的问题就是这种情况,那么请考虑d.items()
按照他的建议进行排序.
Emm*_*uel 40
另一个基于列表理解的单行程序:
>>> 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}]
Run Code Online (Sandbox Code Playgroud)
这里因为我们可以使用dict
比较,所以我们只保留不在初始列表的其余部分的元素(这个概念只能通过索引访问n
,因此使用enumerate
).
Sco*_*pil 15
有时旧式循环仍然有用.这段代码比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])
Run Code Online (Sandbox Code Playgroud)
stp*_*tpk 15
如果您在嵌套字典(如反序列化的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]
Run Code Online (Sandbox Code Playgroud)
the*_*eye 10
如果您想保留订单,那么您可以这样做
from collections import OrderedDict
print OrderedDict((frozenset(item.items()),item) for item in data).values()
# [{'a': 123, 'b': 1234}, {'a': 3222, 'b': 1234}]
Run Code Online (Sandbox Code Playgroud)
如果订单无关紧要,那么你可以做到
print {frozenset(item.items()):item for item in data}.values()
# [{'a': 3222, 'b': 1234}, {'a': 123, 'b': 1234}]
Run Code Online (Sandbox Code Playgroud)
MSe*_*ert 10
如果可以使用第三方软件包,则可以使用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}]
Run Code Online (Sandbox Code Playgroud)
它保留了原始列表的顺序,而ut也可以通过使用较慢的算法(O(n*m)
其中n
原始列表中的元素和原始列表中m
的唯一元素代替O(n)
)来处理诸如字典之类的不可散列的项目。如果键和值都是可哈希的,则可以使用该key
函数的参数为“唯一性测试”创建可哈希的项(以便它在中起作用O(n)
)。
对于字典(比较起来与顺序无关),您需要将其映射到另一个类似的数据结构,例如frozenset
:
>>> list(unique_everseen(l, key=lambda item: frozenset(item.items())))
[{'a': 123}, {'b': 123}]
Run Code Online (Sandbox Code Playgroud)
请注意,您不应该使用简单的tuple
方法(不进行排序),因为相等的字典不一定具有相同的顺序(即使在Python 3.7中也保证了插入顺序 -而不是绝对顺序):
>>> d1 = {1: 1, 9: 9}
>>> d2 = {9: 9, 1: 1}
>>> d1 == d2
True
>>> tuple(d1.items()) == tuple(d2.items())
False
Run Code Online (Sandbox Code Playgroud)
如果键不可排序,甚至对元组进行排序也可能不起作用:
>>> d3 = {1: 1, 'a': 'a'}
>>> tuple(sorted(d3.items()))
TypeError: '<' not supported between instances of 'str' and 'int'
Run Code Online (Sandbox Code Playgroud)
我认为比较这些方法的性能可能会很有用,因此我做了一个小型基准测试。基准图是时间与列表大小的比较,该图表基于不包含重复项的列表(可以随意选择,如果添加一些或大量重复项,运行时不会发生明显变化)。这是一个对数-对数图,因此涵盖了整个范围。
绝对时间:
与最快方法有关的时间安排:
从第二种方法thefourtheye最快在这里。unique_everseen
具有key
功能的方法排在第二位,但这是保留顺序的最快方法。jcollado和thefourtheye的其他方法几乎一样快。对于unique_everseen
没有较长列表的情况,使用无键的方法和Emmanuel和Scorpil的解决方案的速度非常慢,O(n*n)
而不是表现得较差O(n)
。stpk的方法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)
Run Code Online (Sandbox Code Playgroud)
为了完整起见,以下是仅包含重复项的列表的时间安排:
# 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)}
Run Code Online (Sandbox Code Playgroud)
除了unique_everseen
没有key
功能外,时序不会有明显变化,在这种情况下,这是最快的解决方案。但是,这是具有不可散列值的函数的最佳情况(因此不具有代表性),因为它的运行时取决于列表中唯一值的数量:O(n*m)
在这种情况下,该值仅为1,因此在中运行O(n)
。
免责声明:我是的作者iteration_utilities
。
如果您在工作流程中使用Pandas,一种选择是直接将字典列表提供给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}]
Run Code Online (Sandbox Code Playgroud)
不是通用答案,但如果您的列表碰巧按某个键排序,如下所示:
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}]
Run Code Online (Sandbox Code Playgroud)
那么解决方案很简单:
import itertools
result = [a[0] for a in itertools.groupby(l)]
Run Code Online (Sandbox Code Playgroud)
结果:
[{'a': {'b': 31}, 't': 1},
{'a': {'b': 145}, 't': 2},
{'a': {'b': 25231}, 't': 2},
{'a': {'b': 112}, 't': 3}]
Run Code Online (Sandbox Code Playgroud)
适用于嵌套字典并(显然)保留顺序。