1

I have the the following input variables: 'PATH', which is an array containing keys to create to and 'output_content' which is a dictionary where the keys should be created in, 'output content' already contains key-value pairs, and they should not be lost. For example: considering PATH = ['patterns', 'pattern_1', 'children', 'pattern_1_1'], the resulting 'output_content' dictionary should look like this:

{
    'patterns': {
        'pattern_1': {
            'children': {
                'pattern_1_1': {}
            }
        }
    },
    'already_existing_key': 'already_existing_value'
}

I've tried the following:

current_dict = output_content
for key in PATH[1:]:
    if key not in current_dict:
        current_dict[key] = {}
    current_dict = current_dict[key]

But soon i realized that the it just wouldn't work because output_content is changing according to current_dict, so the resulting output will be just {}.

netcat_dd
  • 27
  • 6
  • I don't see any issue with your code, except you wrote `PATH[1:]` instead of just `PATH`. Try it again with just `PATH` and it should work correctly. – Stef Mar 15 '23 at 09:54
  • 1
    `output_content = {'already_existing_key': 'already_existing_value'}; PATH = ['patterns', 'pattern_1', 'children', 'pattern_1_1']; for key in PATH: ...; print(output_content)` will print `{'already_existing_key': 'already_existing_value', 'patterns': {'pattern_1': {'children': {'pattern_1_1': {}}}}}` – Stef Mar 15 '23 at 09:56
  • @Stef unfortunately, there is a problem, at least for me. It is when the `current_dict = current_dict[key]` part executes, the current_dict is being assigned to `{}` at the end of each loop. So, my program ends up with 'current_dict == {}'. – netcat_dd Mar 15 '23 at 10:26
  • @Stef also, i am sorry i didn't pointed that out, but the reason behind me putting `PATH[1:]` instead of `PATH` is that i wanted to skip first element of the `PATH` variable. That is required in my specific case. – netcat_dd Mar 15 '23 at 10:30
  • Alternatively to all the answers below, you could create a nested dict containing only the path, and then merge the two nested dicts with a function from this related question: [How to merge dictionaries of dictionaries?](https://stackoverflow.com/questions/7204805/how-to-merge-dictionaries-of-dictionaries) – Stef Mar 15 '23 at 12:28
  • What you just said about `PATH[1:]` seems inconsistent with the example you gave, in which `PATH = ['patterns', 'pattern_1', 'children', 'pattern_1_1']` and `output_content` ends up as `{ 'patterns': { 'pattern_1': { 'children': { 'pattern_1_1': {} } } }, 'already_existing_key': 'already_existing_value' }` – Stef Mar 15 '23 at 12:32

3 Answers3

0

Two remarks with your code:

  • You should iterate on the whole list PATH, not just PATH[1:]. I see no reason to skip the first element.
  • If the condition key in current_dict is realised, then you should make sure that the associated value is a dict.
def add_path_to_dict(d, path):
    for k in path:
        if k not in d:
            d[k] = {}
        elif not isinstance(d[k], dict):
            return
        d = d[k]

d = {'z': 42}
add_path_to_dict(d, 'abcdefgh')
print(d)
# {'z': 42, 'a': {'b': {'c': {'d': {'e': {'f': {'g': {'h': {}}}}}}}}}

d = {'a': {'b': {'c':{'z': 42}}}}
add_path_to_dict(d, 'abcdefgh')
print(d)
# {'a': {'b': {'c': {'z': 42, 'd': {'e': {'f': {'g': {'h': {}}}}}}}}}

d = {'a': {'b': {'c':42}}}
add_path_to_dict(d, 'abcdefgh')
print(d)
# {'a': {'b': {'c': 42}}}

Further suggestions:

  • Instead of the empty return, you could return 'Some error value' or you could raise ValueError(f'Key {k} already in dict and its value is not a dict.')
Stef
  • 13,242
  • 2
  • 17
  • 28
0

As pointed out in the comments, there isn't much wrong with your code except the unnecessary [1:] slice of the path.

If you're looking for a more concise way to write the code, you can use the dict.setdefault method to avoid having to test if a key already exists:

output_content = {'already_existing_key': 'already_existing_value'}
path = ['patterns', 'pattern_1', 'children', 'pattern_1_1']
current_dict = output_content
for key in path:
    current_dict = current_dict.setdefault(key, {})
print(output_content)

Demo: https://replit.com/@blhsing/UnusedDiscreteScriptinglanguages

blhsing
  • 91,368
  • 6
  • 71
  • 106
0

Both answers by @Stef and @blhsing modify the original dict, a behaviour which you may not want. Here's another solution if you need a copy:

def add_path(path, dict_):
    copied = dict_.copy()
    dict_ = copied
    
    for index, element in enumerate(path):
        dict_ = dict_.setdefault(element, {})
        
        if not isinstance(dict_, dict):
            break
    
    return copied
InSync
  • 4,851
  • 4
  • 8
  • 30