Python3 Pop Nested Json Key Give a String Path

Question:

Use Case

I need to Pop() a nested JSON key based on a variable length string that is passed to my script.

Example

Given the dictionary {'IP': {'key1': 'val1', 'key2': 'val2'}} how can Pop() the key1 value out of the dictionary if my script is passed the path as IP.key1

Note this would need to work given any number of keys.

So far I have found this code snippet on Stack which works in finding the key, but I can’t get this to actually remove the key:

data = {'IP': {'key1': 'val1', 'key2': 'val2'}}
lst = ['IP', 'key1'] # Convert the string of keys to an array

def get(d,l):
    if len(l)==1: return d[l[0]]
    return get(d[l[0]],l[1:])

print(get(data, lst))

Also, I am working in a restricted environment, so using any niche libs is not an option.

I feel like there is probably an easy way to adapt this to pop the full path, but the recursion is hurting my head. Any thoughts?

EDIT

The answer from Filip Müller seems to be working for me. But he didn’t provide much context so I am updating this to demonstrate a more complex data set and the function I used based on Filip’s answer

data = {'IP': {'key1': {'key3': {'key4': 'val4', 'key5': 'val5'}}, 'key2': 'val2'}}
lst = ['IP', 'key1', 'key3', 'key4']


def popKey(d, l):
    current_level = d
    for key in l[:-1]:
        print(key)
        current_level = current_level[key]
        print(current_level)
    current_level.pop(l[-1])
    return d


print(popKey(data, lst))
Asked By: Joe

||

Answers:

Maybe something like this:

def get(d, lst):
    for i in range(len(lst) - 1):
        d = d[lst[i]]
    d.pop(lst[-1])
    return data


print(get(data, lst))

Output:

{'IP': {'key2': 'val2'}}
Answered By: funnydman

Check this out!

data = {'IP': {'key1': {'key3':'val1','key4':'val2'}, 'key2': 'val2'}}
lst = ['IP', 'key1','key4']

temp = data
for i in range(len(lst) - 1):
    d = temp[lst[i]]
    temp = d
d.pop(lst[-1])
print(data)

Output :

{'IP': {'key1': {'key3': 'val1'}, 'key2': 'val2'}}
Answered By: pradipghevaria
data = {'IP': {'key1': 'val1', 'key2': 'val2'}}
lst = ['IP', 'key1']

current_level = data
for key in lst[:-1]:
    current_level = current_level[key]
current_level.pop(lst[-1])

Explanation

I’ll use the more complex example you provided to explain how this works. The first part of the task is to get to the dictionary from which the key should actually be removed.

{
  'IP': {
    'key1': {
      'key3': {
        'key4': 'val4',
        'key5': 'val5'
        }
      },
    'key2': 'val2'
    }
}

path = ['IP', 'key1', 'key3', 'key4']

In this example, in order to remove the key 'key4', we first need to get to the dictionary that contains this key, which is the dictionary under 'key3'. If we had this specific dictionary in a variable, say d, we could just call d.pop('key4').

'key3': {
  'key4': 'val4',
   'key5': 'val5'
  }

The path to this dictionary is data['IP']['key1']['key3']. The algorithm, instead of going directly like this, starts at the root dictionary and goes one level deeper with every iteration of the for loop. So, after the first iteration current_level is data['IP']. After the next one, it becomes data['IP']['key1']. (Because since current_level is already data['IP'], current_level = current_level['key1'] is indeed the same as data['IP']['key1'].)

This process is repeated until the needed dictionary is found. That means doing this for every element in the list that specifies the path, instead of the last one, because the last one is no more a dictionary, but a key in the dictionary that we search for. (lst[:1] is Python’s way of saying all elements from lst except the last one.)

Then finally, we simply pop the necessary key (the last element in the list, in other words lst[-1]) from the dictionary to which it actually belongs, the one the algorithm found in the first step.

Answered By: Filip Müller

A human-friendly code for your use case, I’ve enclosed the [‘key1′,’key2’] in an array so that it makes more sense, but you can change the code accordingly if you don’t want to enclose it.

data = {'IP': {'key1': 'val1', 'key2': 'val2'}}
lst = ['IP', ['key1','key2']] # Convert the string of keys to an array

def remove_key_from_dict(data,list):
   for nested_key in list[1]:
     parent_key = list[0]
     del data[parent_key][nested_key]
    
   print(data)    

remove_key_from_dict(data, lst)

OUTPUT:

{'IP': {}}
Answered By: Shahrukh lodhi