MikG
MikG

Reputation: 1019

Python key match between dictionary and nested dictionary and write new value to nested dictionary

I have two dictionaries, one is a standard key/value pair dictionary and the other is a nested OrderedDict with common key names to the standard dictionary, but has empty values.

I am looking for a way to extract the values from the standard key/value dictionary (properties_dict) and drop these values into my nested dictionary (blank_dict) where the keys match.

I've looked into using 'isinstance' to iterate over values which form other lists/dictionaries, but now I'm beginning to wonder if I am going down the wrong path and possibly overcomplicating it. The other problem I have is one value 'Variant' is in list form, so was looking for a way to pop each value out of the list as it traverses the OrderedDict.

I have added my attempt below.

from collections import OrderedDict


def walkdicts(blankdict, propsdict):
    for k, v in blankdict.items():
        for k2, v2 in propsdict.items():
            if k == k2:
                blankdict[k] = v2
        if isinstance(v, dict):
            walkdicts(v, propsdict)
        elif isinstance(v, list):
            for i in v:
                walkdicts(i[0], propsdict)

    return blankdict


properties_dict = {'A_ID': '2702',
 'Sys': 'MySystem',
 'Namespace': 'SomeNamespace',
 'Vers': '112A',
 'Variant': ['1','2'],
 'SpecID': 'Target1',
 'Seq': '12345',
 'Match': 'ABCDEFG',
 'Time': '200',
 'Case1': 'A',
 'Type': 'Include',
 'MyRef': '1010',
 'Case2': 'B'}


blank_dict =   OrderedDict([('Main', OrderedDict([
                    ('Vers', ''), 
                    ('Namespace', ''), 
                    ('Sys', ''), 
                    ('A_ID', ''),
                    ('Variant', '1'),
                ('Sec1', OrderedDict([
                    ('RequestID', OrderedDict([
                        ('Case1', ''), 
                        ('Case2', '')])), 
                    ('Variant', ''), 
                    ('MyRef', '')])), 
                ('Sec2', OrderedDict([
                    ('UHD', OrderedDict([
                        ('SpecID', ''), 
                        ('Type', ''), 
                        ('AD2W', OrderedDict([
                            ('Time', ''), 
                            ('Match', ''), 
                            ('Seq', '')]))]))]))]))])


new_dict = walkdicts(blank_dict, properties_dict)

print(new_dict)

Here's the output Ordered dictionary I was looking for:

    new_dict =   OrderedDict([('Main', OrderedDict([
                    ('Vers', '112A'), 
                    ('Namespace', 'SomeNamespace'), 
                    ('Sys', 'MySystem'), 
                    ('A_ID', '2702'),
                    ('Variant', '1'),
                ('Sec1', OrderedDict([
                    ('RequestID', OrderedDict([
                        ('Case1', 'A'), 
                        ('Case2', 'B')])), 
                    ('Variant', '2'), 
                    ('MyRef', '1010')])), 
                ('Sec2', OrderedDict([
                    ('UHD', OrderedDict([
                        ('SpecID', 'Target1'), 
                        ('Type', 'Include'), 
                        ('AD2W', OrderedDict([
                            ('Time', '200'), 
                            ('Match', 'ABCDEFG'), 
                            ('Seq', '12345')]))]))]))]))])

Thanks for your time.

Upvotes: 2

Views: 116

Answers (1)

chuckx
chuckx

Reputation: 6902

Here's a function which returns a nested OrderedDict which matches the structure in template, with the values populated by performing lookups in props:

def populate(template, props, prop_indexes=None):
    if prop_indexes is None:
        prop_indexes = {}
    result = OrderedDict()
    for k, v in template.items():
        if isinstance(v, dict):
            result[k] = populate(v, props, prop_indexes)
            continue
        if k in props.keys():
            if isinstance(props[k], list):
                if k not in prop_indexes:
                    prop_indexes[k] = 0
                index = prop_indexes[k] % len(props[k])
                result[k] = props[k][index]
                prop_indexes[k] += 1
            else:
                result[k] = props[k]
    return result

A few notes:

  • prop_indexes is used to keep track of where we are for each property that's defined as a list of values. Using the modulus (%) operator, we can repeatedly cycle through the list if there are more instances in target than there are values in the list
  • Since props is a dictionary, there's no need to loop through it. Instead, simply check if the key exists.
  • This could still use some work to gracefully handle edge cases and error conditions, but I just wanted to demonstrate a working approach. A major assumption is that template consists only of nested dictionaries.

Using the definitions of blank_dict (slightly modified by adding an extra Variant key, see Note after the output) and properties_dict from your example code, here's a small bit of code to put it all together:

import pprint
pp = pprint.PrettyPrinter()

print("## blank_dict ##")
pp.pprint(blank_dict)

new_dict = populate_new(blank_dict, properties_dict)

print("## new_dict ##")
pp.pprint(new_dict)

And here's the output (pprint doesn't really render OrderedDict all that pretty, but it gets the job done):

## blank_dict ##
OrderedDict([('Main',
              OrderedDict([('Vers', ''),
                           ('Namespace', ''),
                           ('Sys', ''),
                           ('A_ID', ''),
                           ('Variant', ''),
                           ('Sec1',
                            OrderedDict([('RequestID',
                                          OrderedDict([('Case1', ''),
                                                       ('Case2', '')])),
                                         ('Variant', ''),
                                         ('MyRef', '')])),
                           ('Sec2',
                            OrderedDict([('UHD',
                                          OrderedDict([('SpecID', ''),
                                                       ('Type', ''),
                                                       ('Variant', ''),
                                                       ('AD2W',
                                                        OrderedDict([('Time',
                                                                      ''),
                                                                     ('Match',
                                                                      ''),
                                                                     ('Seq',
                                                                      '')]))]))]))]))])
## new_dict ##
OrderedDict([('Main',
              OrderedDict([('Vers', '112A'),
                           ('Namespace', 'SomeNamespace'),
                           ('Sys', 'MySystem'),
                           ('A_ID', '2702'),
                           ('Variant', '1'),
                           ('Sec1',
                            OrderedDict([('RequestID',
                                          OrderedDict([('Case1', 'A'),
                                                       ('Case2', 'B')])),
                                         ('Variant', '2'),
                                         ('MyRef', '1010')])),
                           ('Sec2',
                            OrderedDict([('UHD',
                                          OrderedDict([('SpecID', 'Target1'),
                                                       ('Type', 'Include'),
                                                       ('Variant', '1'),
                                                       ('AD2W',
                                                        OrderedDict([('Time',
                                                                      '200'),
                                                                     ('Match',
                                                                      'ABCDEFG'),
                                                                     ('Seq',
                                                                      '12345')]))]))]))]))])

Note: I added an extra Variant instance to demonstrate populating 3 instances even though there are only 2 values specified in properties_dict

Upvotes: 1

Related Questions