使用更高的键值过滤字典列表并删除重复的字典。

4

我有一个字典列表,例如:

sel_list = [{'a': 8}, {'a': 4}, {'a': 4}, {'b': 8}, {'b': 9}]

我希望删除重复的字典,如果有多个字典具有相同的键但不同的值,则选择具有更高值的字典。
例如:
sel_list = [{'a': 8}, {'b': 9}]

我已尝试过:
[i for n, i in enumerate(sel_list) if i not in sel_list[n + 1:]]

它的结果是:

[{'a': 8}, {'a': 4}, {'b': 8}, {'b': 9}]

我该如何达成我的目标?

那么每个字典都只包含恰好一个键值对? - Willem Van Onsem
结果的顺序重要吗? - Willem Van Onsem
是的,每个字典都包含一个键值对,而顺序并不重要。 - Usman Maqbool
如果顺序不重要,而您想要每个键一个值,为什么不使用dict作为数据结构?为什么要用list?请参见我的答案获取更多细节。 - Reut Sharabani
4个回答

7
我们可以构建一个“字典”,通过每次选择最大值来“折叠”值。像这样:
dummy = object()
maximums = {}
for subd in sel_list:
    for k, v in subd.items():
        cur = maximums.get(k, dummy)
        if cur is dummy or v > cur:
            maximums[k] = v
result = [{k: v} for k, v in maximums.items()]

我们遍历列表中字典的键值对,每次更新最大值字典(maximums)如果该键还不存在或当前值更小。完成遍历后,生成一个包含最大键值对的字典列表。
这种方法适用于所有可排序的类型(数字、字符串等),键必须是可哈希的,但此假设成立,因为在字典列表中,键已经被哈希。
此外,它非常健壮,它会忽略空字典,并且也能处理具有多个键值对的字典,将其视为独立的键值对。
您也可以直接使用maximums:一个包含原始列表中所有键的字典,并将这些键关联到列表中看到的最大值。

1
这看起来非常有趣。我想我要学习一些Python中的隐藏宝石。您能否解释一下为什么要使用object()而不是例如None?直接这样做不是更简单吗:cur = maximums.get(k, None),然后比较:if cur is None or v > cur: - cezar
1
@cezar:从技术上讲,我们可以使用“None”生成带有顺序关系的对象。例如,如果“None”出现一次,我们可能还想将其包含在结果列表中。通过添加一个虚拟值,我们可以区分字典中的“None”和未找到的情况。 - Willem Van Onsem
1
谢谢你的解释。我没有考虑到字典中可能有None作为值的可能性。我会记住这一点,它可能在某些时候拯救我的生命。 - cezar
@WillemVanOnsem 我建议使用 maximums.get(k, v),并且去掉 dummy。我仍然认为 OP 的数据布局有误(请参见我的答案)。 - Reut Sharabani

2

免责声明:我不确定我的解决方案有多少Pythonic

假设dict只包含一个键值对(例如,{"a":2,"b":3}在此处无效)

sel_list = [{'a': 0}, {'a': 4}, {'a': 4}, {'b': 8}, {'b': 9}, {'d': 9}]
result_dict = {}
for item in sel_list:
    key = list(item.keys())[0]
    if key in result_dict:
        if item[key] > result_dict[key][key]:
            result_dict.update({key: item})
    else:
        result_dict.update({key: item})
result_list = [v for k, v in result_dict.items()]
print(result_list)

1

代码:

from collections import defaultdict

sel_list = [{'a': 8}, {'a': 4}, {'a': 4}, {'b': 8}, {'b': 9}]

results = defaultdict(list)

for element in sel_list:
    for key, value in element.items():
        results[key].append(value)

for item_key, item_value in results.items():
    results[item_key] = max(item_value)

print(results)

输出:

defaultdict(<class 'list'>, {'b': 9, 'a': 8})

0
你可以通过使用一个 dict 作为状态,使用 reduce 操作来实现此功能:
from functools import reduce
from itertools import chain

sel_list = [{'a': 8}, {'a': 4}, {'a': 4}, {'b': 8}, {'b': 9}]

# function to aggregate max value item into a `dict`
def agg(d, kv):
    k, v = kv
    d[k] = max(d.get(k, v), v)
    return d

# concatenate all items from all `dict`s
sel_items = chain.from_iterable(map(dict.items, sel_list))

# aggregate using a single `dict` which implicitly holds required semantics
result = reduce(agg, sel_items, {}) # {'a': 8, 'b': 9}  <-- better?

为了获得您的输出格式(在我看来dict方便):

formatted = [dict((item, )) for item in result.items()]

dict 有的语义符合你的利益。我认为它是更好的数据结构。


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