Reputation: 55
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?
Upvotes: 3
Views: 1911
Reputation: 4127
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)
Upvotes: 2
Reputation: 1291
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}]}
Upvotes: 1