如何获得一组字典?

时间:2019-05-09 15:49:32

标签: python dictionary set

我有一个字典列表,但是其中一些是重复的,我想删除它们(重复的)。

字典的键是一个序列号。

一个示例如下:

[{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在其中三个元素的内部安装了三个元素,如图所示。

5 个答案:

答案 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的执行检查明显更快,但更简单(不一定是一件好事)。