从字典中删除重复项

43
我有以下Python 2.7字典数据结构(我无法控制源数据 - 它来自于另一个系统):
{112762853378: 
   {'dst': ['10.121.4.136'], 
    'src': ['1.2.3.4'], 
    'alias': ['www.example.com']
   },
 112762853385: 
   {'dst': ['10.121.4.136'], 
    'src': ['1.2.3.4'], 
    'alias': ['www.example.com']
   },
 112760496444: 
   {'dst': ['10.121.4.136'], 
    'src': ['1.2.3.4']
   },
 112760496502: 
   {'dst': ['10.122.195.34'], 
    'src': ['4.3.2.1']
   },
 112765083670: ...
}
字典键值将始终是唯一的。 dst,src和alias可以重复。所有记录将始终具有dst和src,但并非每个记录都必须具有别名,如第三条记录所示。
在样本数据中,前两条记录中的任何一条都将被删除(哪个被删除对我来说无关紧要)。 第三条记录将被视为唯一的,因为尽管dst和src相同,但缺少alias。
我的目标是删除所有dst,src和alias均已重复的记录 - 不管键是什么。
这个新手怎么做到呢?
此外,我对Python的有限理解将数据结构解释为带有存储在字典中的值的字典...字典的字典,这正确吗?
11个回答

53

你可以遍历字典中的每一个项(键值对),如果该值不在结果字典中,就将它们添加到结果字典中。

input_raw = {112762853378: 
   {'dst': ['10.121.4.136'], 
    'src': ['1.2.3.4'], 
    'alias': ['www.example.com']
   },
 112762853385: 
   {'dst': ['10.121.4.136'], 
    'src': ['1.2.3.4'], 
    'alias': ['www.example.com']
   },
 112760496444: 
   {'dst': ['10.121.4.136'], 
    'src': ['1.2.3.4']
   },
 112760496502: 
   {'dst': ['10.122.195.34'], 
    'src': ['4.3.2.1']
   }
}

result = {}

for key,value in input_raw.items():
    if value not in result.values():
        result[key] = value

print result

16
这是一个不错的起点,但我觉得有必要指出,对于大量数据,它会变得缓慢,因为每次循环时,它都会创建一个新的值列表并进行线性搜索。 - senderle
@senderle:感谢您对速度的想法和评论,如果必要的话,我会考虑这一点。您是否愿意扩展此答案以提高性能? - Bit Bucket
这并没有回答所提出的问题。 - joel3000
@FakeRainBrigand - 它不仅删除dst、src和alias重复的条目。'我的目标是删除所有dst、src和alias都被重复的记录 - 不管键是什么。' 我的答案和下面的一些答案都可以做到这一点。 - joel3000
@joel3000,我仍然不明白为什么Andrew Cow没有做出被要求的东西。正如senderle所强调的那样,这不是一个好的答案,因为算法的问题,但我认为它可以正确地工作。在我看来,你的答案同样复杂且性能不佳。 - eyquem
显示剩余4条评论

6

一个简单的方法是使用每个内部字典中字符串数据的连接作为键创建反向字典。假设您在字典d中有上述数据:

>>> import collections
>>> reverse_d = collections.defaultdict(list)
>>> for key, inner_d in d.iteritems():
...     key_str = ''.join(inner_d[k][0] for k in ['dst', 'src', 'alias'] if k in inner_d)
...     reverse_d[key_str].append(key)
... 
>>> duplicates = [keys for key_str, keys in reverse_d.iteritems() if len(keys) > 1]
>>> duplicates
[[112762853385, 112762853378]]

如果您不想要重复的列表或类似的东西,而只是想创建一个没有重复项的字典,那么您可以使用普通字典而不是defaultdict,并像下面这样重新反转它:

>>> for key, inner_d in d.iteritems():
...     key_str = ''.join(inner_d[k][0] for k in ['dst', 'src', 'alias'] if k in inner_d)
...     reverse_d[key_str] = key
>>> new_d = dict((val, d[val]) for val in reverse_d.itervalues())

4
input_raw = {112762853378:  {'dst': ['10.121.4.136'],
                             'src': ['1.2.3.4'],
                             'alias': ['www.example.com']    },
             112762853385:  {'dst': ['10.121.4.136'],
                             'src': ['1.2.3.4'],
                             'alias': ['www.example.com']    },
             112760496444:  {'dst': ['10.121.4.299'],
                             'src': ['1.2.3.4']    },
             112760496502:  {'dst': ['10.122.195.34'],
                             'src': ['4.3.2.1']    },
             112758601487:  {'src': ['1.2.3.4'],
                             'alias': ['www.example.com'],
                             'dst': ['10.121.4.136']},
             112757412898:  {'dst': ['10.122.195.34'],
                             'src': ['4.3.2.1']    },
             112757354733:  {'dst': ['124.12.13.14'],
                             'src': ['8.5.6.0']},             
             }

for x in input_raw.iteritems():
    print x
print '\n---------------------------\n'

seen = []

for k,val in input_raw.items():
    if val in seen:
        del input_raw[k]
    else:
        seen.append(val)


for x in input_raw.iteritems():
    print x

结果

(112762853385L, {'src': ['1.2.3.4'], 'dst': ['10.121.4.136'], 'alias': ['www.example.com']})
(112757354733L, {'src': ['8.5.6.0'], 'dst': ['124.12.13.14']})
(112758601487L, {'src': ['1.2.3.4'], 'dst': ['10.121.4.136'], 'alias': ['www.example.com']})
(112757412898L, {'src': ['4.3.2.1'], 'dst': ['10.122.195.34']})
(112760496502L, {'src': ['4.3.2.1'], 'dst': ['10.122.195.34']})
(112760496444L, {'src': ['1.2.3.4'], 'dst': ['10.121.4.299']})
(112762853378L, {'src': ['1.2.3.4'], 'dst': ['10.121.4.136'], 'alias': ['www.example.com']})

---------------------------

(112762853385L, {'src': ['1.2.3.4'], 'dst': ['10.121.4.136'], 'alias': ['www.example.com']})
(112757354733L, {'src': ['8.5.6.0'], 'dst': ['124.12.13.14']})
(112757412898L, {'src': ['4.3.2.1'], 'dst': ['10.122.195.34']})
(112760496444L, {'src': ['1.2.3.4'], 'dst': ['10.121.4.299']})

这种解决方案首先创建一个列表 input_raw.iteritems()(与Andrew Cox的答案相同),并需要一个增长的列表 seen,这是缺点。
但第一个缺点无法避免(使用iteritems()不起作用),第二个缺点比在循环的每一轮中重新创建一个列表 result.values() 更轻。


3

另一种反向字典变体:

>>> import pprint
>>> 
>>> data = {
...   112762853378: 
...    {'dst': ['10.121.4.136'], 
...     'src': ['1.2.3.4'], 
...     'alias': ['www.example.com']
...    },
...  112762853385: 
...    {'dst': ['10.121.4.136'], 
...     'src': ['1.2.3.4'], 
...     'alias': ['www.example.com']
...    },
...  112760496444: 
...    {'dst': ['10.121.4.136'], 
...     'src': ['1.2.3.4']
...    },
...  112760496502: 
...    {'dst': ['10.122.195.34'], 
...     'src': ['4.3.2.1']
...    },
... }
>>> 
>>> keep = set({repr(sorted(value.items())):key
...             for key,value in data.iteritems()}.values())
>>> 
>>> for key in data.keys():
...     if key not in keep:
...         del data[key]
... 
>>> 
>>> pprint.pprint(data)
{112760496444L: {'dst': ['10.121.4.136'], 'src': ['1.2.3.4']},
 112760496502L: {'dst': ['10.122.195.34'], 'src': ['4.3.2.1']},
 112762853378L: {'alias': ['www.example.com'],
                 'dst': ['10.121.4.136'],
                 'src': ['1.2.3.4']}}

3
在我看来,这个东西很不错但也很复杂。 - eyquem
似乎{'src':['1.2.3.4'], 'dst':['10.121.3.1236']}{'src':['10.121.3.1236'], 'dst':['1.2.3.4']}被视为彼此的重复项... - senderle
@senderle。发现得好!我已经修复了,顺便说一下,虽然这个解决方案很紧凑,但与其他一些解决方案相比,效率相当低下。 - ekhumoro

2

在寻找一一对应唯一性的方式中,使用字典是最好的方式,以所需唯一值作为键,创建一个反转字典,将您的值组成为键 - 然后使用中间结果重新创建“反转”字典。

dct = {112762853378: 
   {'dst': ['10.121.4.136'], 
    'src': ['1.2.3.4'], 
    'alias': ['www.example.com']
   },
 112762853385: 
   {'dst': ['10.121.4.136'], 
    'src': ['1.2.3.4'], 
    'alias': ['www.example.com']
   },
 112760496444: 
   {'dst': ['10.121.4.136'], 
    'src': ['1.2.3.4']
   },
 112760496502: 
   {'dst': ['10.122.195.34'], 
    'src': ['4.3.2.1']
   },
   }

def remove_dups (dct):
    reversed_dct = {}
    for key, val in dct.items():
        new_key = tuple(val["dst"]) + tuple(val["src"]) + (tuple(val["alias"]) if "alias" in val else (None,) ) 
        reversed_dct[new_key] = key
    result_dct = {}
    for key, val in reversed_dct.items():
        result_dct[val] = dct[val]
    return result_dct

result = remove_dups(dct)

2
dups={}

for key,val in dct.iteritems():
    if val.get('alias') != None:
        ref = "%s%s%s" % (val['dst'] , val['src'] ,val['alias'])# a simple hash
        dups.setdefault(ref,[]) 
        dups[ref].append(key)

for k,v in dups.iteritems():
    if len(v) > 1:
        for key in v:
            del dct[key]

不得不更新一下。如果我正确理解了问题,现在应该可以工作了。 - joel3000

1

我使用压缩字典方法解决了这个问题:

dic = {112762853378: 
    {'dst': ['10.121.4.136'], 
     'src': ['1.2.3.4'], 
     'alias': ['www.example.com']
    },
112762853385: 
    {'dst': ['10.121.4.136'], 
     'src': ['1.2.3.4'], 
     'alias': ['www.example.com']
    },
112760496444: 
    {'dst': ['10.121.4.136'], 
     'src': ['1.2.3.4']
    },
112760496502: 
    {'dst': ['10.122.195.34'], 
     'src': ['4.3.2.1']
    }
}

result = {k:v for k,v in dic.items() if list(dic.values()).count(v)==1}

1
但是它肯定会删除具有重复值的所有键的所有出现... - mirekphd

1
from collections import defaultdict

dups = defaultdict(lambda : defaultdict(list))

for key, entry in data.iteritems():
    dups[tuple(entry.keys())][tuple([v[0] for v in entry.values()])].append(key)

for dup_indexes in dups.values():
    for keys in dup_indexes.values():
        for key in keys[1:]:
            if key in data:
                del data[key]

2
这个的复杂度是O(n^3)! - FaCoffee

0
我会先创建一个键列表,然后迭代这些键并将它们放入新字典中:
input_raw = {112762853378: 
   {'dst': ['10.121.4.136'], 
    'src': ['1.2.3.4'], 
    'alias': ['www.example.com']
   },
 112762853385: 
   {'dst': ['10.121.4.136'], 
    'src': ['1.2.3.4'], 
    'alias': ['www.example.com']
   },
 112760496444: 
   {'dst': ['10.121.4.136'], 
    'src': ['1.2.3.4']
   },
 112760496502: 
   {'dst': ['10.122.195.34'], 
    'src': ['4.3.2.1']
   }
}

filter = list(set(list(input_raw.keys())))

fixedlist = {}

for i in filter:
    fixedlist[i] = logins[i]

-1

你可以使用

set(dictionary) 

为了解决您的问题。


4
可能会出现错误:TypeError: unhashable type: 'dict'。 - Barak Schoster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接