Python:基于键值条件从字典列表中删除元素

3

我正在尝试从字典中删除一个元素,但只有在满足条件时才能执行。

例如:

dd = {'11': [{'xx': 259, 'yy': 1, 'channels': 55}, {'xx': 260, 'yy': 2, 'channels': 35}], '22': [{'xx': 259, 'yy': 1, 'channels': 40}, {'xx': 303, 'yy': 2, 'channels': 30}]}

场景:

channels = 60

上述嵌套字典中有两个键为xx = 259的元素,我希望只保留离channels = 60值更接近的一个元素。
期望输出:
dd = {'11': [{'xx': 259, 'yy': 1, 'channels': 55}, {'xx': 260, 'yy': 2, 'channels': 35}], '22': [{'xx': 303, 'yy': 2, 'channels': 30}]}

我已经尝试过以下方法:
channels = 60
for key, values in dd.items():
    key = str(key)
    if key in dd:
        for elem in dd[key]:
            if elem['xx'] == 259:
                print(elem)
                # some logic here to check the closest value to channels and remove the furthest one

这将输出:

{'xx': 259, 'yy': 1, 'channels': 55}
{'xx': 259, 'yy': 1, 'channels': 40}

更新方法:

    channels = 60
    xList = []
    for key, values in dd.items():
        key = str(key)
        if key in dd:
            for elem in dd[key]:
                if elem['xx'] == 259:
                    xList.append(elem['channels'])


    result =min(xList, key=lambda x: abs(x - 60))

    # find and remove elem from dict
    for key, values in dd.items():
        key = str(key)
        if key in dd:
            for elem in dd[key]:
                if elem['xx'] == 259:
                    if elem['channels'] == result:
                        pass
                    else:
                        print("delete this elem: ", elem)
                        dd[key].remove(elem)
    print(dd)

输出:

{'11': [{'xx': 259, 'yy': 1, 'channels': 55}, {'xx': 260, 'yy': 2, 'channels': 35}], '22': [{'xx': 303, 'yy': 2, 'channels': 30}]}

我达到了最终目标,但我觉得这可以进一步改善。需要帮忙吗?


请在您的问题中更新您尝试从字典中删除条目的方法。 - quamrana
@quamrana 更新了尝试过的内容。 - Lee Chun
我已经得到了期望的结果,但是是否可以通过更好、更快的方法来改进呢? - Lee Chun
2个回答

2

我认为这个做法可以满足你的需求,而不必在第二个循环中搜索匹配项(这是remove()函数所必须做的),它通过保留所有候选元素的键和索引,然后删除除了最小delta值之外的所有元素来实现。

channels = 60
xList = []
for key, values in dd.items():
    for index,elem in enumerate(values):
        if elem['xx'] == 259:
            xList.append((abs(elem['channels']-channels),key,index)) # capture delta, key, and index

# this drops the lowest difference entry and sorts the remaining (deletions) in reverse index order
xList=sorted([(key,index) for (delta,key,index) in sorted(xList)[1:]],reverse=True)

# the deletions must be done from highest index to lowest index
for key,index in xList:  # pull indexes from highest to lowest
    del dd[key][index]

print(dd)

(编辑:以上代码早期版本有一个括号放错了位置)

这个版本删除除每个键最近的之外的所有内容,而不是整体删除:

channels = 60
for key, values in dd.items():
    xList = []
    for index,elem in enumerate(values):
        if elem['xx'] == 259:
            xList.append((abs(elem['channels']-channels),index)) # capture delta and index
    # this drops the lowest difference entry and sorts the remaining (deletions) in reverse index order
    xList=sorted([index for (delta,index) in sorted(xList)[1:]],reverse=True)
    for index in xList:  # pull indexes from highest to lowest
        del dd[key][index]

我将处理过程放在循环内,并从选择列表中删除了键。

太棒了,只有一个小问题。我的解决方案只删除超过“channels”值的字典元素,而你的删除了除一个之外的所有元素。 - Lee Chun
我以为你想要删除除最接近的条目之外的所有条目。我有另一个解决方案,它保留了每个键的最接近匹配,但被拒绝了。 - RufusVS
明白了。请问是否需要将每个键保留最接近的匹配项,而删除超出范围的内容?谢谢! :) - Lee Chun
我修改了代码,其中有一个括号放错了位置。 - RufusVS
1
@Lee Chun - 我在我的答案中添加了备用代码,以执行每个键内的函数而不是整体函数(请注意,第一组代码也需要进行小修改)。 - RufusVS

1

我找到了一个比你目前使用的更紧凑的解决方案。代码本身并不是一个清晰的示例,但它运行得相当快。我还扩展了你的示例字典,这样我们就可以测试有多个键为259的子字典的情况。

from collections import defaultdict

CHANNELS = 60

dd = {'11': [{'xx': 259, 'yy': 1, 'channels': 55},
             {'xx': 259, 'yy': 2, 'channels': 30},
             {'xx': 260, 'yy': 3, 'channels': 35}],
      '22': [{'xx': 259, 'yy': 1, 'channels': 40},
             {'xx': 259, 'yy': 2, 'channels': 45},
             {'xx': 303, 'yy': 3, 'channels': 30}]}


# Nasty list comprehension to get the items where 'xx' == 259

ddlst = [[key, idx, valin['channels']] for key, val in dd.items()
         for idx, valin in enumerate(val) if valin['xx'] == 259]

ddlstkey = defaultdict(list)
for key, *vals in ddlst:
    ddlstkey[key].append(vals)
ddlstkey = dict(ddlstkey)

# Delete from the previous list the element that is closer to channels = 60

for k, i in ddlstkey.items():
    minval = min(ddlstkey[k], key=lambda x: abs(x[:][1] - CHANNELS))
    ddlstkey[k].remove(minval)

# Finally delete the non-minimum items from original dictionary

for key, vals in ddlstkey.items():
    for vl in reversed(vals):
        del dd[key][vl[0]]

print(dd)

这将输出:

{'11': [{'xx': 259, 'yy': 1, 'channels': 55}, {'xx': 260, 'yy': 3, 'channels': 35}], '22': [{'xx': 259, 'yy': 2, 'channels': 45}, {'xx': 303, 'yy': 3, 'channels': 30}]}

1
我喜欢你的解决方案,但是如果在给定键中有多个元素需要删除,这将不起作用,因为第一个删除后,随后元素的索引将发生变化。它们必须从高索引到低索引进行删除。我的解决方案类似,但我喜欢你使用min从删除列表中消除保留者的方法。我认为你只需要改变你的for语句来使用reversed(ddlst) - RufusVS
@RufusVS非常感谢您的优秀反馈!我在随后的删除中忽略了此细节,reversed(ddlst)确实是必要的。非常高兴您喜欢我的解决方案,我也很喜欢您的! - panadestein

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