如果其中一个值不唯一,从 dict 列表中删除元素的 Pythonic 方法是什么?

时间:2021-06-03 20:24:59

标签: python

删除某些键的非唯一元素的 Pythonic 方法是什么?

假设有一个字典列表,例如:

[
    {'a': 1, 'b': 'j'},
    {'a': 2, 'b': 'j'},
    {'a': 3, 'b': 'i'}
]

预期的输出将删除第二个元素,因为键 b 在多个元素中等于 j。因此:

[
    {'a': 1, 'b': 'j'},
    {'a': 3, 'b': 'i'}
]

这是我尝试过的:

input = [
    {'a': 1, 'b': 'j'},
    {'a': 2, 'b': 'j'},
    {'a': 3, 'b': 'i'}
]

output = []
for input_element in input:
    if not output:
        output.append(input_element)
    else:
        for output_element in output:
            if input_element['b'] != output_element['b']:
                output.append(input_element)

如果是元组列表,解决方案是否会更简单,例如:

[(1, 'j'), (2, 'j'), (3, 'i')]

# to produce
[(1, 'j'), (3, 'i')]

5 个答案:

答案 0 :(得分:3)

这是一种使用 any() 和列表理解的方法:

代码:

l=[
    {'a': 1, 'b': 'j'},
    {'a': 2, 'b': 'j'},
    {'a': 3, 'b': 'i'}
]

new_l = []

for d in l:
    if any([d['b'] == x['b'] for x in new_l]):
        continue
    new_l.append(d)

print(new_l)

输出:

[{'a': 1, 'b': 'j'}, {'a': 3, 'b': 'i'}]

答案 1 :(得分:1)

def drop_dup_key(src, key):
    ''' src is the source list, and key is a function to obtain the key'''
    keyset, result = set(), []
    for elem in src:
        keyval = key(elem)
        if keyval not in keyset:
             result.append(elem)
             keyset.add(keyval)
    return result

像这样使用它:

drop_dup_key(in_list, lambda d: return d.get('b'))

答案 2 :(得分:1)

您可以定义一个自定义容器类来实现 __eq____hash__ 魔术方法。这样,您可以使用 set 删除“重复项”(根据您的标准)。这不一定能保持秩序。

from itertools import starmap
from typing import NamedTuple

class MyTuple(NamedTuple):
    a: int
    b: str

    def __eq__(self, other):
        return self.b == other.b

    def __hash__(self):
        return ord(self.b)


print(set(starmap(MyTuple, [(1, 'j'), (2, 'j'), (3, 'i')])))

输出:

{MyTuple(a=3, b='i'), MyTuple(a=1, b='j')}
>>> 

答案 3 :(得分:1)

我建议这种实现:

_missing = object()
def dedupe(iterable, selector=_missing):
    "De-duplicate a sequence based on a selector"
    keys = set()
    if selector is _missing: selector = lambda e: e
    for e in iterable:
        if selector(e) in keys: continue
        keys.add(selector(e))
        yield e

优点:

  • 返回一个生成器:
    它只是懒惰地迭代原始集合一次。那可能有用 和/或在某些情况下执行,特别是如果您将链接 额外的查询操作。

    input = [{'a': 1, 'b': 'j'}, {'a': 2, 'b': 'j'}, {'a': 3, 'b': 'i'}]
    s = dedupe(input, lambda x: x['b'])
    s = map(lambda e: e['a'], s)
    sum(s) # Only now the list is iterated. Result: 4
    
  • 接受任何类型的迭代:
    无论是列表、集合、字典还是自定义的可迭代类。您可以从中构造任何集合类型,而无需多次迭代。

    d = {'a': 1, 'b': 1, 'c': 2}
    
    {k: v for k, v in dedupe(d.items(), lambda e: e[1])}
    # Result (dict): {'a': 1, 'c': 2}
    
    {*dedupe(d.items(), lambda e: e[1])}
    # Result (set of tuples): {('a', 1), ('c', 2)}
    
  • 采用可选的选择器函数(或任何可调用的):
    这使您可以灵活地在许多不同的上下文中使用任何自定义逻辑或类型重新使用此函数。如果没有选择器,它会比较整个元素。

    # de-duping based on absolute value:
    (*dedupe([-3, -2, -2, -1, 0, 1, 1, 2, 3, 3], abs),)
    # Result: (-3, -2, -1, 0)
    
    # de-duping without selector:
    (*dedupe([-3, -2, -2, -1, 0, 1, 1, 2, 3, 3]),)
    # Result: (-3, -2, -1, 0, 1, 2, 3)
    

答案 4 :(得分:0)

元组与字典的比较不是很准确,因为元组只包含字典值,不包含键,我相信你问的是重复的键:值对。

这是一个我相信可以解决您的问题的解决方案,但可能不是尽可能像 Pythonic。

seen = set()
kept = []

for d in x:
     keep = True
     for k, v in d.items():
         if (k, v) in seen:
             keep = False
             break
         seen.add((k, v))
     if keep:
         kept.append(d)

print(kept)

输出:

[{'a': 1, 'b': 'j'}, {'a': 3, 'b': 'i'}]
相关问题