Sebastian Goslin
Sebastian Goslin

Reputation: 497

Updating only the value n-nested dictionary

I'm trying update update the value of a nested dictionary within a for loop, so it doesn't generate a new dictionary every time, I'm pretty new to traversing nested structures so bear with me. Each value is located in a list:

My list:

id_list = ['asf245', 'kjb456', '235sdg']

My dictionary:

temp = {"ent": {"type": "IDN", "attributes": [{"ent": {"id": "abc123"}}], "limit": 20}}

Ideally I would append each update dictionary to a dataframe and then update it with the new value:

Ideal output:

    temp = {"ent": {"type": "IDN", "attributes": [{"ent": {"id": "asf245"}}], "limit": 20}}

    temp = {"ent": {"type": "IDN", "attributes": [{"ent": {"id": "kjb456"}}], "limit": 20}} 

    temp = {"ent": {"type": "IDN", "attributes": [{"ent": {"id": "235sdg"}}], "limit": 20}}

Where temp gets appended to a dataframe every iteration then gets overwritten with the new value:

I've tried:

import collections

def update(d, u):

    for k, v in u.items():
        if isinstance(v, collections.Mapping):
            d[k] = update(d.get(k, {}), v)
        else:
            d[k] = v
    return d

print(update(temp, 'Apples')) <- "run this through a loop"

But running this through a visualizer I can see that it doesn't go deep enough, and I don't truly have a good understanding of it, if anyone could explain it that would be awesome.

Upvotes: 0

Views: 59

Answers (2)

balderman
balderman

Reputation: 23815

Here. The result of the function is a list of dicts (with modified id)

import copy


def clone_dict(d, ids):
    result = []
    for id in ids:
        clone = copy.deepcopy(d)
        clone['ent']['attributes'][0]['ent']['id'] = id
        result.append(clone)
    return result


temp = {"ent": {"type": "IDN", "attributes": [{"ent": {"id": "abc123"}}], "limit": 20}}
ids = ['x', 'y', 'z']

print(clone_dict(temp, ids))

output

[{'ent': {'attributes': [{'ent': {'id': 'x'}}], 'type': 'IDN', 'limit': 20}}, {'ent': {'attributes': [{'ent': {'id': 'y'}}], 'type': 'IDN', 'limit': 20}}, {'ent': {'attributes': [{'ent': {'id': 'z'}}], 'type': 'IDN', 'limit': 20}}]

A generic approach below

import copy


def clone_dict(src_dict, values_to_inject, path_elements):
    """ Clone a dict N times and replace a nested field

    :param src_dict: Used as 'template'
    :param values_to_inject: List of values to inject
    :param path_elements: List of path elements. Used in dict navigation
    :return: A list of cloned modified dicts
    """
    result = []
    for value in values_to_inject:
        clone = copy.deepcopy(src_dict)
        temp = clone[path_elements[0]]
        for path_element in path_elements[1:-1]:
            temp = temp[path_element]
        temp[path_elements[-1]] = value
        result.append(clone)
    return result


src_dict = {"ent": {"type": "IDN", "attributes": [{"ent": {"id": "abc123"}}], "limit": 20}}
values_to_inject = ['x', 'y', 'z']
path_elements = ['ent', 'attributes', 0, 'ent', 'id']

print(clone_dict(src_dict, values_to_inject, path_elements))

Upvotes: 1

gold_cy
gold_cy

Reputation: 14216

Here is a more generic solution involving recursion. It takes a dictionary to update, the key to update, and the value that you want to update.

def update(to_update, key, val):
    for k, v in to_update.items():
        if k == key:
            to_update[k] = val
        else:
            if isinstance(v, dict):
                update(v, key, val)
            elif isinstance(v, list):
                for item in v:
                    if isinstance(item, (dict, list)):
                        update(item, key, val)
                    else:
                        continue
            else:
                continue
    return to_update


for id_ in id_list:
    new = update(temp, 'id', id_)
    print(new)

{'ent': {'type': 'IDN', 'attributes': [{'ent': {'id': 'asf245'}}], 'limit': 20}}
{'ent': {'type': 'IDN', 'attributes': [{'ent': {'id': 'kjb456'}}], 'limit': 20}}
{'ent': {'type': 'IDN', 'attributes': [{'ent': {'id': '235sdg'}}], 'limit': 20}}

Upvotes: 1

Related Questions