Home > Software design >  How to delete values in a dict based on the presence of child values
How to delete values in a dict based on the presence of child values

Time:04-25

I have a dict that looks like this:

OrderedDict([('arguments', {'index': 1, 'parent': None}), ('controls', {'index': 2, 'parent': None}), ('examples', {'index': 3, 'parent': None}), ('journal', {'index': 4, 'parent': None}), ('journal/config', {'index': 5, 'parent': 'journal'}), ('journal/journaltest', {'index': 6, 'parent': 'journal'}), ('procs', {'index': 7, 'parent': None}), ('processor', {'index': 8, 'parent': None}), ('prediction', {'index': 9, 'parent': None}), ('reports', {'index': 10, 'parent': None}), ('tooki', {'index': 11, 'parent': None}), ('tooki/help', {'index': 12, 'parent': 'tooki'}), ('tooki/lime', {'index': 13, 'parent': 'tooki'}), ('tooki/medium', {'index': 14, 'parent': 'tooki'}), ('tooki/share', {'index': 15, 'parent': 'tooki'}), ('tooki/trigger', {'index': 16, 'parent': 'tooki'})])

How can I iterate over the elements of this dict, and exclude any element that has "'parent' : None" and no child element (meaning, no other element in this dict has this one as a parent).

Expected output:

OrderedDict([('journal', {'index': 4, 'parent': None}), ('journal/config', {'index': 5, 'parent': 'journal'}), ('journal/journaltest', {'index': 6, 'parent': 'journal'}), ('tooki', {'index': 11, 'parent': None}), ('tooki/help', {'index': 12, 'parent': 'tooki'}), ('tooki/lime', {'index': 13, 'parent': 'tooki'}), ('tooki/medium', {'index': 14, 'parent': 'tooki'}), ('tooki/share', {'index': 15, 'parent': 'tooki'}), ('tooki/trigger', {'index': 16, 'parent': 'tooki'})])

CodePudding user response:

ins = OrderedDict(...)

parents = set(elem['parent'] for elem in ins.values() if elem['parent'] is not None)

d = OrderedDict()
for key, value in ins.items():
    if value['parent'] is not None:
        d[key] = value
    elif any(ins.get(k) is not None for k in parents):
        d[key] = value

Using a set of parent keys remove lot of complexity. In your case, there are lot of elements, but only two keys will be checked.

CodePudding user response:

A really dumb way of doing this:

x = OrderedDict(blah blah blah)

keys_to_exclude = []
for key1, val1 in x.items():
    if val1['parent'] is None:
        remove = True
        for key2, val2 in x.items():
            if val2['parent'] == key1:
                remove = False  
        if remove:
            keys_to_exclude.append(key1)

for key in keys_to_exclude:
    x.pop(key, None) 

This code obviously had O(n^2) complexity. A smarter way of doing this would be adding a 'child' key for each element beforehand and the complexity would be O(n).

CodePudding user response:

Here is something that working for me. The order of the final output is not the same, but I was able to meet the requirement of excluding the key value pairs according to the conditions you had mentioned.

a = OrderedDict([('arguments', {'index': 1, 'parent': None}),
                 ('controls', {'index': 2, 'parent': None}),
                 ('examples', {'index': 3, 'parent': None}),
                 ('journal', {'index': 4, 'parent': None}),
                 ('journal/config', {'index': 5, 'parent': 'journal'}),
                 ('journal/journaltest', {'index': 6, 'parent': 'journal'}),
                 ('procs', {'index': 7, 'parent': None}),
                 ('processor', {'index': 8, 'parent': None}),
                 ('prediction', {'index': 9, 'parent': None}),
                 ('reports', {'index': 10, 'parent': None}),
                 ('tooki', {'index': 11, 'parent': None}),
                 ('tooki/help', {'index': 12, 'parent': 'tooki'}),
                 ('tooki/lime', {'index': 13, 'parent': 'tooki'}),
                 ('tooki/medium', {'index': 14, 'parent': 'tooki'}),
                 ('tooki/share', {'index': 15, 'parent': 'tooki'}),
                 ('tooki/trigger', {'index': 16, 'parent': 'tooki'})])
od = OrderedDict()
for each in a:
    if (parent := a[each].get('parent', None)):
        od[parent] = a[parent]

for each in a:
    if (element := a[each].get('parent', None)):
        od[each] = a[each]

print(od)

I did not comprehension for simplicity of understanding the code. The first loop saves the parents to a new OrdereDict. The second saves the children. Do let me know if your requirements are not met.

  • Related