AJG
AJG

Reputation: 129

Removing nulls and empty objects of mixed data types from a dictionary with a nested structure

Off the back of a similar question I asked, how would one go about cleaning a dictionary containing a variety of datatypes: nulls, empty lists, empty dicts etc, at varying levels of nesting E.g.

{
  "key":"value",
  "key1": {},
  "key2": [],
  "key3": True,
  "key4": False,
  "key5": None,
  "key6": [1,2,3],
  "key7": {
    "subkey": "subvalue"
  },
  "key8": {
    "subdict": {
      "subdictkey": "subdictvalue", 
      "subdictkey1": {},
      "subdictkey2": [],
      "subdictkey3": None
    }
  }
}

Becomes:

{
  "key":"value",
  "key3": True,
  "key4": False,
  "key6": [1,2,3],
  "key7": {
    "subkey": "subvalue"
  },
  "key8": {
    "subdict": {
      "subdictkey": "subdictvalue"
    }
  }
}

The solution should be good for n levels of nesting (not just 1 level). Obviously I want to avoid nested loops (particularly as n could equal 3 or 4), is the only solution flattening the structure? Is there a more elegant way of going about it?

Edit: Building on @Ch3steR answer and accounting for the issue I encountered with a list containing a null, this is the final working function:

def recur(n_dict,new_d={}):
    global counter
    for key,val in n_dict.items():
        if val or isinstance(val,bool) or (isinstance(val,list) and any(elem is not None for elem in val)):
                if (isinstance(val,list) and any(elem is None for elem in val)):
                   counter=counter+1
                else:
                    new_d={**new_d,**{key:val}}
                    if isinstance(val,dict):
                        new_d[key]=recur(val)

    return new_d

Upvotes: 0

Views: 69

Answers (1)

Ch3steR
Ch3steR

Reputation: 20669

You can use Recursion when you are dealing with an arbitrarily nested dictionary.

Try this.

def recur(n_dict,new_d={}):
    for key,val in n_dict.items():
        if val or isinstance(val,bool):
            new_d={**new_d,**{key:val}}
            if isinstance(val,dict):
                new_d[key]=recur(val)
    return new_d

a={
  "key":"value",
  "key1": {},
  "key2": [],
  "key3": True,
  "key4": False,
  "key5": None,
  "key6": [1,2,3],
  "key7": {
    "subkey": "subvalue"
  },
  "key8": {
    "subdict": {
      "subdictkey": "subdictvalue", 
      "subdictkey1": {},
      "subdictkey2": [],
      "subdictkey3": None
    }
  }
}

print(recur(a))
{'key': 'value',
 'key3': True,
 'key4': False,
 'key6': [1, 2, 3],
 'key7': {'subkey': 'subvalue'},
 'key8': {'subdict': {'subdictkey': 'subdictvalue'}}}

recur(n_dict,new_d={}) uses mutable default argument.

Note:

Never mutate new_d in-place or you will encounter this problem.

One of the way to check if your default argument is changed is use __defaults__

>>>recur(a)
>>>recur.__defaults__
({},) 
>>>recur(a)
>>>recur.__defaults__
({},) 

Upvotes: 2

Related Questions