我有一个字典列表,但是其中一些是重复的,我想删除它们(重复的)。
字典的键是一个序列号。
一个示例如下:
[{1: {a:[1,2,3], b: 4}},
{2: {a:[4,5,6], d: 5}},
{3: {a:[1,2,3], b: 4}},
.....,
{1000: {a:[2,5,1], b: 99}},
]
考虑上一个示例,我想获得:
[{1: {a:[1,2,3], b: 4}},
{2: {a:[4,5,6], d: 5}},
.....,
{1000: {a:[2,5,1], b: 99}},
]
实际上,具有键1和3的字典的值相同。
我尝试了一个集合,但是由于dict是不可散列的类型,因此我无法这样做。
我该如何解决该问题?
编辑
就我而言,字典中的项目数不固定,因此我可以:
[{1: {a:[1,2,3], b: 4}},
{2: {a:[4,5,6], d: 5}},
.....,
{1000: {a:[2,5,1], b: 99, c:["a","v"]}},
]
带键的字典100在其中三个元素的内部安装了三个元素,如图所示。
答案 0 :(得分:4)
要避免@jdehesa解决方案的局限性,其中[1, 2]
将被视为与(1, 2)
的重复,您可以使用pprint.pformat
保留数据类型,而不是序列化数据结构体。由于pprint.pformat
按键对项排序,按项对项排序,因此{1: 2, 3: 4}
将被视为与{3: 4, 1: 2}
相同,但是[1, 2]
将不被视为与{{1}的重复项}:
(1, 2)
from pprint import pformat
lst = [
{1: {'a': [1, 2, 3], 'b': 4}},
{2: {'a': [4, 5, 6], 'd': 5}},
{3: {'b': 4, 'a': [1, 2, 3]}},
{4: {'a': (4, 5, 6), 'd': 5}},
]
seen = set()
output = []
for d in lst:
for k, v in d.items():
signature = pformat(v)
if signature not in seen:
seen.add(signature)
output.append({k: v})
变为:
output
答案 1 :(得分:3)
您也许可以使用这样的函数将您的对象变成可哈希的对象:
def make_hashable(o):
if isinstance(o, dict):
return frozenset((k, make_hashable(v)) for k, v in o.items())
elif isinstance(o, list):
return tuple(make_hashable(elem) for elem in o)
elif isinstance(o, set):
return frozenset(make_hashable(elem) for elem in o)
else:
return o
然后,您保留一组可见的对象,并仅保留每个字典的键,其中包含以前没有看到的对象:
lst = [
{1: {'a':[1,2,3], 'b': 4}},
{2: {'a':[4,5,6], 'd': 5}},
{3: {'a':[1,2,3], 'b': 4}},
]
seen = set()
result_keys = []
for elem in lst:
keep_keys = []
for k, v in elem.items():
v_hashable = make_hashable(v)
if v_hashable not in seen:
seen.add(v_hashable)
keep_keys.append(k)
result_keys.append(keep_keys)
result = [{k: elem[k] for k in keys} for elem, keys in zip(lst, result_keys) if keys]
print(result)
# [{1: {'a': [1, 2, 3], 'b': 4}}, {2: {'a': [4, 5, 6], 'd': 5}}]
请注意,正如评论中的blhsing所指出的那样,这有一些局限性,例如考虑将(1, 2)
和[1, 2]
等于{1: 2}
和{{1 }}。另外,某些类型可能无法转换为等效的可哈希类型。
编辑:正如a_guest所建议的那样,您可以通过返回类型本身以及{(1, 2)}
中的可哈希对象来解决类型不明确的问题:
make_hashable
如果您不需要查看可哈希对象,则可以轻松地进行严格的类型比较。请注意,在这种情况下,甚至def make_hashable(o):
t = type(o)
if isinstance(o, dict):
o = frozenset((k, make_hashable(v)) for k, v in o.items())
elif isinstance(o, list):
o = tuple(make_hashable(elem) for elem in o)
elif isinstance(o, set):
o = frozenset(make_hashable(elem) for elem in o)
return t, o
和{1, 2}
之类的东西也会有所不同。
答案 2 :(得分:1)
您可以通过子类化dict
来定义字典的自定义哈希值:
class MyData(dict):
def __hash__(self):
return hash((k, repr(v)) for k, v in self.items())
l = [
{1: {'a': [1, 2, 3], 'b': 4}},
{2: {'a': [4, 5, 6], 'd': 5}},
{3: {'b': 4, 'a': [1, 2, 3]}},
{4: {'a': (4, 5, 6), 'd': 5}},
]
s = set([MyData(*d.values()) for d in l])
这是假设列表中的所有词典只有一对键值对。
答案 3 :(得分:0)
我不知道您的列表有多大,列表中有多少重复项,但是,以防万一,这是基本解决方案。
可能效率不高,但是您不必担心元素类型:
import datetime as dt
data = [
{1: {"b": 4, "a":[1,2,3]}},
{2: {"a":[4,5,6], "d": 5}},
{3: {"a":[1,2,3], "b": 4}},
{4: {'a': dt.datetime(2019, 5, 10), 'd': set([4])}},
{5: {'a': dt.datetime(2019, 5, 10), 'd': set([4])}},
{6: {"a":[2,5,1], "b": 99}},
{7: {"a":[5,2,1], "b": 99}},
{8: {"a":(5,2,1), "b": 99}}
]
seen = []
output = []
for d in data:
for k, v in d.items():
if v not in seen:
seen.append(v)
output.append({k:v})
>>> print(output)
[{1: {'a': [1, 2, 3], 'b': 4}},
{2: {'a': [4, 5, 6], 'd': 5}},
{4: {'a': datetime.datetime(2019, 5, 10, 0, 0), 'd': {4}}},
{6: {'a': [2, 5, 1], 'b': 99}},
{7: {'a': [5, 2, 1], 'b': 99}},
{8: {'a': (5, 2, 1), 'b': 99}}]
答案 4 :(得分:0)
这是我能够假设嵌套字典像这样的最简单的解决方案
{1: {'a': [1,2,3,5,79], 'b': 234 ...}}
只要字典中的唯一容器是像{'a': [1,2,3..]}
这样的列表,那么它将起作用。或者,您也可以添加一个简单的检查,如下面的功能所示。
def serialize(dct): # this is the sub {'a': [1,2,3]} dictionary
tmp = []
for value in dct.values():
if type(value) == list:
tmp.append(tuple(value))
else:
tmp.append(value)
return tuple(tmp)
def clean_up(lst):
seen = set()
clean = []
for dct in lst:
# grabs the 1..1000 key inside the primary dictionary
# assuming there is only 1 key being the "id" or the counter etc...
key = list(dct.keys())[0]
serialized = serialize(dct[key])
if serialized not in seen:
seen.add(serialized)
clean.append(dct)
return clean
因此,函数serialize
抓取嵌套的字典并根据内容创建一个简单的元组。然后检查其是否在set
“已看到”中以验证其唯一性。
使用一些随机值生成数据集,只是因为
lst = []
for i in range(1,1000):
dct = {
i: {
random.choice(string.ascii_letters): [n for n in range(random.randint(0,i))],
random.choice(string.ascii_letters): random.randint(0,i)
}
}
lst.append(dct)
运行基准测试
%timeit clean_up(lst)
3.25 ms ± 17.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit jdhesa(lst)
126 ms ± 606 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
如所见,功能clean_up
的执行检查明显更快,但更简单(不一定是一件好事)。