如何删除列表中唯一的,然后重复的词典?

时间:2009-11-13 03:19:52

标签: python list dictionary

鉴于以下列表包含一些重复和一些独特的词典,首先删除唯一词典的最佳方法是什么,然后将重复的词典减少为单个实例?我得说我最近刚开始进入Python,但它使这个项目所以更容易。我对这类问题感到有点难过。

所以我的列表看起来像这样:

[{  'file': u'/file.txt',
    'line': u'line 666',
    'rule': u'A DUPLICATE RULE'}

{   'file': u'/file.txt',
    'line': u'line 666',
    'rule': u'A DUPLICATE RULE'}

{   'file': u'/uniquefile.txt',
    'line': u'line 999',
    'rule': u'A UNIQUE RULE'}]

我想要的是最后,列表应该如下:

[{  'file': u'/file.txt',
    'line': u'line 666',
    'rule': u'A DUPLICATE RULE'}]

7 个答案:

答案 0 :(得分:4)

一个想法是对数据进行排序。假设inputdata是您上面的列表:

from itertools import groupby
from operator import itemgetter

inputdata.sort(key=itemgetter(*inputdata[0])) # ensures order
print [k for k, g in groupby(inputdata) if len(list(g)) > 1]

打印:

[{'line': u'line 666', 'file': u'/file.txt', 'rule': u'A DUPLICATE RULE'}]

答案 1 :(得分:2)

如果每个项目的字段相同,我总是喜欢使用对象而不是dicts。

所以,我定义了一个类:

class rule(object):
    def __init__(self, file, line, rule):
        self.file = file
        self.line = line
        self.rule = rule

    #Not a "magic" method, just a helper for all the methods below :)
    def _tuple_(self):
        return (self.file, self.line, self.rule)

    def __eq__(self, other):
        return cmp(self, other) == 0

    def __cmp__(self, other):
        return cmp(self._tuple_(), rule._tuple_(other))

    def __hash__(self):
        return hash(self._tuple_())

    def __repr__(self):
        return repr(self._tuple_())

现在,创建这些对象的列表并对其进行排序。 ruledict_list可以是您问题中的示例数据。

rules = [rule(**r) for r in ruledict_list]
rules.sort()

循环遍历(已排序)列表,随时删除唯一对象。最后,创建一个集合,以删除重复项。循环也将删除每个重复对象中的一个,但这并不重要。

pos = 0
while(pos < len(rules)):
    while pos < len(rules)-1 and rules[pos] == rules[pos+1]:
        print "Skipping rule %s" % rules[pos]
        pos+=1
    rules.pop(pos)
rule_set = set(rules)

答案 2 :(得分:1)

我会创建另一个字典,使用现有字典作为键,并将出现次数作为值。 (Python不允许将字典用作开箱即用的字典键,但在this answer中有几种方法可以实现这一点。)然后,只需迭代它并选择键即可其中值大于1。

当然,使用字典作为键依赖于其内容不会随时间变化 - 至少在您需要使用结果字典时。 (这就是Python本身不支持它的原因。)

答案 3 :(得分:1)

另一种方法是根据冻结的项目集为每个dict数据创建一个计数器:

from operator import itemgetter
from collections import defaultdict

counter = defaultdict(int)
for d in inputdata:
    counter[frozenset(d.iteritems())] += 1

result = [dict(item) for item, count in counter.iteritems() if count > 1]
print result

我认为这是迄今为止最好的答案,因为它很容易理解并且能够线性地工作。

答案 4 :(得分:1)

>>> import itertools
>>> list(a[0] for a in itertools.groupby(sorted(data)) if len(list(a[1])) > 1)
[{'file': u'/file.txt', 'line': u'line 666', 'rule': u'A DUPLICATE RULE'}]

检查这个可能比len(list(a [1]))更优化。

编辑:我添加了对sorted的调用。

答案 5 :(得分:1)

这个答案是基于Steven Huwig的回答。它与他类似,但我在列表中使用sorted(),以便groupby()正常工作。

另外,因为他说“检查这个可能比len(list(a [1]))更好的方式。”,我决定用其他方法来检查非唯一项目。我尝试在迭代器上调用.next()方法两次,而不是强制整个列表。如果它工作两次,迭代器中至少有两个项目,我们就完成了它;如果我们在第一次或第二次调用StopIteration时得到.next()异常,则迭代器中只有零个或一个项。 (实际上,因为我们从itertools.groupby得到了这个迭代器,我们知道它至少会有一个项目。)

此外,我没有使用像a[0]a[1]这样的显式元组索引,而是使用了元组解包,因为这就是酷孩子们最近所做的事情。

最后,我不是使用生成器表达式来计算列表,而是使用list()强制它扩展到列表中,我只是使用列表推导。

data = [
    {
        'file': u'/file.txt',
        'line': u'line 666',
        'rule': u'A DUPLICATE RULE'
    },

    {   'file': u'/uniquefile.txt',
        'line': u'line 999',
        'rule': u'A UNIQUE RULE'
    },

    {   'file': u'/file.txt',
        'line': u'line 666',
        'rule': u'A DUPLICATE RULE'
    },

]

from itertools import groupby

def notunique(itr):
    try:
        itr.next()
        itr.next()
        return True
    except StopIteration:
        return False

def unique_list(lst):
    return [key for key, itr in groupby(sorted(lst)) if notunique(itr)]

print(unique_list(data))

答案 6 :(得分:0)

另一个选择是创建自己的数据结构而不是使用dict。如果您这样做,则可以覆盖__cmp____eq____hash__。这将使您能够在其所有荣耀中使用“set”数据类型。

这是一个可能的实现,虽然我没有对我提供的哈希例程的质量做出承诺:

class Thing(object):
    def __init__(self, file, line, rule):
        self.file = file
        self.line = line
        self.rule = rule

    def __cmp__(self, other):
        result = cmp(self.file, other.file)
        if result == 0:
            result = cmp(self.line, other.line)
        if result == 0:
            result = cmp(self.rule, other.rule)
        return result

    def __eq__(self, other):
        return cmp(self, other) == 0

    def __hash__(self):
        return hash(self.file) * hash(self.line) * hash(self.rule)

    def __str__(self):
        return ', '.join([self.file, self.line, self.rule])

things = [ Thing(u'/file.txt', u'line 666', u'A DUPLICATE RULE'),
  Thing(u'/file.txt', u'line 666', u'A DUPLICATE RULE'),
  Thing(u'/uniquefile.txt', u'line 999', u'A UNIQUE RULE')]

duplicate_things = set()
unique_things = set()
for t in things:
    if t in unique_things:
        duplicate_things.add(t)
    else:
        unique_things.add(t)

如果您需要返回列表,只需从结果集中构建一个:

unique_things = list(unique_things)
duplicate_things = list(duplicate_things)

要创建自己的类,需要更多代码,但如果您的程序变得复杂,可能会为您提供其他选项。

修改

好的,今晚我的手比我的眼睛快,但我认为这个编辑解决了@nosklo指出的问题