简体   繁体   中英

python: remove element from list of dict based on key-value condition

Im trying to remove a element from a dict only if the condition is met.

For example,

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}]}

Scenario:

channels = 60

there are two elements with key xx = 259 in the nested dict above, I wish to keep only one element which is closer to the channels = 60 value.

expected output:

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

I tried so far:

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

Which outputs:

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

UPDATED APPROACH:

    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)

OUTPUT:

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

I managed to achieve the end goal, but I feel like this can certainly be improved. Any help?

I think this does what you're looking for, without having to search for matching items in the second loop (which remove() has to do), by keeping the keys and indexes of all candidate elements, and then deleting all but the one with the lowest delta value.

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)

(Edit: earlier version of code above had a misplaced parenthesis)

This version removes all but the closest within each key instead of overall:

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]

(I put processing within the loop and removed keys from the selection list)

I have found a solution a bit more compact than the one you have so far. The code itself is not an example of clarity, but it works reasonably fast. I have also extended your example dictionary, so we can test the case in which there are several sub-dictionaries with key 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)

This outputs:

{'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}]}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM