如何从Python中的列表中删除重复的词典?

时间:2013-08-28 20:53:13

标签: python list python-2.7 dictionary duplicates

我有一个按特定键排序的字典列表。每个字典包含32个元素,列表中有超过4000个字典。我需要代码来处理列表并返回一个删除了所有重复项的新列表。

来自这些链接的方法:

不要帮助我,因为字典不可用。

有什么想法?如果您需要更多信息,请发表评论,我将添加信息。

修改

重复字典可以是list[dictionary][key]具有相同值的任意两个字典。


好的,这是对需要者的详细解释。

我有一个像这样的字典列表:

[ {
    "ID" : "0001",
    "Organization" : "SolarUSA",
    "Matchcode" : "SolarUSA, Something Street, Somewhere State, Whatev Zip",
    "Owner" : "Timothy Black",
   }, {
    "ID" : "0002",
    "Organization" : "SolarUSA",
    "Matchcode" : "SolarUSA, Something Street, Somewhere State, Whatev Zip",
    "Owner" : "Johen Wilheim",
   }, {
    "ID" : "0003",
    "Organization" : "Zapotec",
    "Matchcode" : "Zapotec, Something Street, Somewhere State, Whatev Zip",
    "Owner" : "Simeon Yurrigan",
   } ]

在此列表中,第一个和第二个词典是重复的,因为它们的Matchcodes是相同的。

现在,此列表按以下代码排序:

# sort_by is "Matchcode"
def sort( list_to_be_sorted, sort_by ):
    return sorted(list_to_be_sorted, key=lambda k: k[sort_by])

所以我有一个按Matchcode排序的整齐的词典列表。现在我只需要迭代列表,访问list[dictionary][key]并在两个键值匹配时删除重复项。

8 个答案:

答案 0 :(得分:10)

正如您可以使用tuple获取与list相当的可清除值,您可以使用frozenset获取与dict相当的可清除值。唯一的技巧是你需要将d.items()而不是d传递给构造函数。

>>> d = {'a': 1, 'b': 2}
>>> s = frozenset(d.items())
>>> hash(s)
-7588994739874264648
>>> dict(s) == d
True

然后你可以使用你最喜欢的解决方案。如果您需要保留订单等,请将它们转储到set或使用OrderedSetunique_everseen食谱。例如:

>>> unique_sets = set(frozenset(d.items()) for d in list_of_dicts)
>>> unique_dicts = [dict(s) for s in unique_sets]

或者,保留顺序并使用键值:

>>> sets = (frozenset(d.items()) for d in list_of_dicts)
>>> unique_sets = unique_everseen(sets, key=operator.itemgetter(key))
>>> unique_dicts = [dict(s) for s in unique_sets]

当然,如果你有嵌套的列表或dicts,你必须递归转换,就像你对列表一样。

答案 1 :(得分:6)

使用itertools.groupby()按键值对词典进行分组,然后从每个组中获取第一个项目。

import itertools

data =[ {
    "ID" : "0001",
    "Organization" : "SolarUSA",
    "Matchcode" : "SolarUSA, Something Street, Somewhere State, Whatev Zip",
    "Owner" : "Timothy Black",
   }, {
    "ID" : "0002",
    "Organization" : "SolarUSA",
    "Matchcode" : "SolarUSA, Something Street, Somewhere State, Whatev Zip",
    "Owner" : "Johen Wilheim",
   }, {
    "ID" : "0003",
    "Organization" : "Zapotec",
    "Matchcode" : "Zapotec, Something Street, Somewhere State, Whatev Zip",
    "Owner" : "Simeon Yurrigan",
   } ]


print [g.next() for k,g in itertools.groupby(data, lambda x: x['Matchcode'])]

给出结果

[{'Owner': 'Timothy Black',  
  'Organization': 'SolarUSA', 
  'ID': '0001',  
  'Matchcode': 'SolarUSA, Something Street, Somewhere State, Whatev Zip'},

 {'Owner': 'Simeon Yurrigan', 
  'Organization': 'Zapotec', 
  'ID': '0003', 
  'Matchcode':'Zapotec, Something Street, Somewhere State, Whatev Zip'}]

我相信这就是你要找的东西。

编辑:我更喜欢unique_justseen解决方案。它更短,更具描述性。

答案 2 :(得分:4)

对于现在消除歧义的问题,这个答案是不正确的。


答案 3 :(得分:1)

  

所以我有一个整齐的字典列表按Matchcode排序。现在我只需要迭代列表,访问列表[字典] [密钥]并在两个键值匹配时删除重复项。

我还不完全确定这意味着什么。听起来你说它们将始终按照你想要用来统一的相同键进行排序。如果是这样,您可以使用unique_justseen中使用的sort list_of_dicts,使用>>> list(unique_justseen(list_of_dicts, key=itemgetter('Matchcode'))) [{'ID': '0001', 'Matchcode': 'SolarUSA, Something Street, Somewhere State, Whatev Zip', 'Organization': 'SolarUSA', 'Owner': 'Timothy Black'}, {'ID': '0003', 'Matchcode': 'Zapotec, Something Street, Somewhere State, Whatev Zip', 'Organization': 'Zapotec', 'Owner': 'Simeon Yurrigan'}] 中使用的相同键功能,例如itertools recipes

使用编辑过的问题中的示例unique_justseen

>>> list_of_dicts.sort(key=itemgetter('Owner'))
>>> list(unique_justseen(list_of_dicts, key=itemgetter('Matchcode')))
[{'ID': '0002',
  'Matchcode': 'SolarUSA, Something Street, Somewhere State, Whatev Zip',
  'Organization': 'SolarUSA',
  'Owner': 'Johen Wilheim'},
 {'ID': '0003',
  'Matchcode': 'Zapotec, Something Street, Somewhere State, Whatev Zip',
  'Organization': 'Zapotec',
  'Owner': 'Simeon Yurrigan'},
 {'ID': '0001',
  'Matchcode': 'SolarUSA, Something Street, Somewhere State, Whatev Zip',
  'Organization': 'SolarUSA',
  'Owner': 'Timothy Black'}]

如果它们按不同的键排序到我们无法识别的那个,那么它们被排序的事实根本不相关,unique_everseen赢了工作:

>>> list_of_dicts.sort(key=itemgetter('Owner'))
>>> list(unique_everseen(list_of_dicts, key=itemgetter('Matchcode')))
[{'ID': '0002',
  'Matchcode': 'SolarUSA, Something Street, Somewhere State, Whatev Zip',
  'Organization': 'SolarUSA',
  'Owner': 'Johen Wilheim'},
 {'ID': '0003',
  'Matchcode': 'Zapotec, Something Street, Somewhere State, Whatev Zip',
  'Organization': 'Zapotec',
  'Owner': 'Simeon Yurrigan'}]

但是你必须改为使用Owner食谱:

Matchcode

(当然这次我们有0001而不是0001,因为在key上排序之后,它现在是{{1}}而不是第二个的第一个值。)


字典不可清除的事实在这里不相关,因为配方只是将键函数的结果存储在它们的集合中,因此只要存储在键{{1}}的值是可清除的,一切都很好。

答案 4 :(得分:1)

现在,如果特定键匹配,我们可以看到两个字典是重复的,问题非常简单。只是迭代字典;跟踪你看过的钥匙,最后用独特的钥匙制作新的清单。

import collections
def get_unique_items(list_of_dicts, key="Matchcode"):
    # Count how many times each key occurs.
    key_count = collections.defaultdict(lambda: 0)
    for d in list_of_dicts:
        key_count[d[key]] += 1

    # Now return a list of only those dicts with a unique key.
    return [d for d in list_of_dicts if key_count[d[key]] == 1]

请注意,我在这里使用defaultdict来计算每个键的出现次数(还有其他方法可以做到这一点,但我认为这是最干净的,个人的)。我没有使用set跟踪“已访问”密钥的原因是,您将获得列表中每个密钥的一个副本,包括重复密钥。这意味着您必须保留第二个 set,以跟踪真正重复的密钥(当您遇到密钥时已经存在于“已访问”密钥中的密钥),所以你不要不包括它们。

另一方面,如果您想要的只是获取每个给定密钥所看到的第一个字典,无论是否重复,set方法都可以正常工作,例如在{{ 3}}

答案 5 :(得分:0)

seen_values = set()
without_duplicates = []
for d in list_of_dicts:
    value = d[key]
    if value not in seen_values:
        without_duplicates.append(d)
        seen_values.add(value)

答案 6 :(得分:0)

基本上你需要像no_dup(checked_val_extrator, list)这样的东西,其中no_dup可能是这样的:

def no_dup(extractor, lst):
   "keeps only first elements encountered for any particular extracted value using =="
   known = set()
   res = []
   for item in lst:
     if extractor(item) in known: continue
     known.add(extractor(item))
     res.append(item)
   return res

答案 7 :(得分:-1)

我并不是100%明确你想要实现的目标,但是:

删除所有重复的字典条目

只要您不介意合并所有词典,

import itertools
dict(itertools.chain(*map(lambda x: x.items(), list_of_dictionaries)))