rororo
rororo

Reputation: 845

Looping over nested dictionary and delete if condition is not met (python)

I have a list of nested dictionaries:

[{'a': 1,
  'b': 'string',
  'c': [{'key1': 80,
         'key2': 'string',
         'key3': 4033},
        {'key1': 324,
         'key2': 'string',
         'key3': 4034,
         'key4': 1}]},
 {'a': 1,
  'b': 'string',
  'c': [{'key1': 80,
         'key2': 'string',
         'key3': 4033},
        {'key1': 324,
         'key2': 'string',
         'key3': 4034,
         'key4': 1,
         'key5': 2}]}]

Please not that the values of key c is a list of dictionaries again. Now I want to filter out from this list all dictionaries with key c, that do not contain key1, key2, key3 & key4.

I thought of looping first over the first, second, and so on dict in the list, and then looping over the nested dicts that have c as a key. Then, if the dict inside c does not meet my requirement, I delete it.

Therefore my code would be:

for j in range(len(mydict)):
    for i in range(len(mydict[j]["c"])):
        if not all (k in mydict[j]["c"][i] for k in ("key1", "key2", "key3", "key4")):
            del(mydict[j]["c"][i])

But I am getting a IndexError: list index out of range error. Where is my mistake?

My desired output would be:

[{'a': 1,
  'b': 'string',
  'c': [{'key1': 324,
         'key2': 'string',
         'key3': 4034,
         'key4': 1}]},
 {'a': 1,
  'b': 'string',
  'c': [{'key1': 324,
         'key2': 'string',
         'key3': 4034,
         'key4': 1,
         'key5': 2}]}]

Upvotes: 0

Views: 616

Answers (3)

jdlk07
jdlk07

Reputation: 53

If you wanted another perspective on it:

def remove_keys(mydict):
    mydict2 = mydict
    keys = ['key1', 'key2', 'key3', 'key4']
    for xIndex, x in enumerate(mydict):
        for yIndex, y in enumerate(x['c']):
            if not all(key in y.keys() for key in keys):
                del mydict2[xIndex]['c'][yIndex]
    return mydict2

Returns a new dictionary with the modifications.

Upvotes: 1

iGian
iGian

Reputation: 11193

Just to have another option:

keep = {'key1', 'key2', 'key3', 'key4'}
for h in mydict:
    h['c'] = [ e for e in h['c'] if len(keep - set(e.keys())) == 0 ]

Upvotes: 1

tobias_k
tobias_k

Reputation: 82929

The problem is that with for i in range(len(mydict[j]["c"])): you are iterating the lists in the dict while at the same time removing from those lists. Instead, you can replace the inner loop with a list comprehension:

for d in mydict:
    d['c'] = [d2 for d2 in d['c']
                 if all(k in d2 for k in ("key1", "key2", "key3", "key4"))]

Upvotes: 2

Related Questions