简体   繁体   中英

generating config styled file from list of dictionaries in most pythonic way

I have a list of dictionaries as,

[{'section_id': 1, 
'parent_sec_id': 0, 
'sec_name': 'apple', 
'key1': 'val1'},
{'section_id': 2, 
'parent_sec_id': 0, 
'sec_name': 'banana', 
'key2': 'val2'},
{'section_id': 3, 
'parent_sec_id': 1, 
'sec_name': 'orange', 
'key3': 'val3'},
{'section_id': 4, 
'parent_sec_id': 2, 
'sec_name': 'guava', 
'key4': 'val4'},
{'section_id': 5, 
'parent_sec_id': 3, 
'sec_name': 'grape', 
'key5': 'val5'}]

Each dictionary has an identifier for the dictionaries as 'section_id' and also a key as 'parent_section_id' which tells whether its a child dictionary of any other dictionary. So basically, if the parent_section_id is set to 0 (zero) then its a parent dictionary otherwise its the child of the dictionary mentioned with that section id.
Now from the above list of dictionaries, I was asked to achieve the following format (yes i was asked, part of interview):

apple
{
    'key1': 'val1'

    orange
    {
        'key3': 'val3'

        grape
        {
            'key5': 'val5'
        }
    }

}

banana
{
    'key2': 'val2'

    guava
    {
        'key4': 'val4'
    }
}

I was told this is the format used to write config files for any program. I'm just curious as to what could have been the best possible way to generate a file from this list of dictionaries.

You can recursively output sections whose parent_sec_id matches the given parent ID, with the output from the children indented:

def transform(sections, parent=0):
    output = []
    indent = ' ' * 4
    for section in sections:
        if section['parent_sec_id'] == parent:
            output.extend((section['sec_name'], '{'))
            for key, value in section.items():
                if key not in ('section_id', 'parent_sec_id', 'sec_name'):
                    output.append("%s'%s': '%s'" % (indent, key, value))
            output.extend(indent + line for line in transform(sections, section['section_id']))
            output.append('}')
    return output

Assuming your sample list of dicts is stored as variable sections , then '\\n'.join(transform(sections)) would return:

apple
{
    'key1': 'val1'
    orange
    {
        'key3': 'val3'
        grape
        {
            'key5': 'val5'
        }
    }
}
banana
{
    'key2': 'val2'
    guava
    {
        'key4': 'val4'
    }
}

Not very elegant, but you can collect your items in a collections.defaultdict() , then output your dictionary paths to a new file.

The basic idea is to first collection your root parent ids with the value of 0, the add the proceeding child dictionaries to these roots. You can use the last value in each list for the parent id of the most recently added item.

Demo:

from collections import defaultdict

def group_sections(data, parent_id, section_id, root_id = 0):
    """Groups sections into dictionary of lists, connecting on parent keys"""

    groups = defaultdict(list)

    # Separate root and rest of children
    roots = [dic for dic in data if dic[parent_id] == root_id]
    children = [dic for dic in data if dic[parent_id] != root_id]

    # Add roots first
    for root in roots:
        groups[root[section_id]].append(root)

    # Append children next
    for child in children:
        for key, collection in list(groups.items()):

            # Get most recently added child
            recent = collection[-1]

            # Only add child if equal to parent
            if child[parent_id] == recent[section_id]:
                groups[key].append(child)

    # Filter out result dictionary to not include parent and section ids
    return {
        k1: [
            {k2: v2 for k2, v2 in d.items() if k2 != parent_id and k2 != section_id}
            for d in v2
        ]
        for k1, v2 in groups.items()
    }

def write_config_file(filename, data, name_key):
    """Write config file, using dictionary of lists"""

    # Writes n tabs to string
    tab_str = lambda n: "\t" * n

    with open(filename, mode="w") as config_file:
        for group in data.values():
            tabs = 0
            for dic in group:
                for key in dic:

                    # Write name key
                    if key == name_key:
                        config_file.write(
                            "%s%s\n%s{\n" % (tab_str(tabs), dic[key], tab_str(tabs))
                        )
                        tabs += 1

                    # Otherwise write key-value pairs
                    else:
                        config_file.write(
                            "%s'%s': '%s'\n" % (tab_str(tabs), key, dic[key])
                        )

            # Write ending curly braces
            for i in range(tabs - 1, -1, -1):
                config_file.write("%s}\n" % (tab_str(i)))

if __name__ == "__main__":
    list_dicts = [
        {"section_id": 1, "parent_sec_id": 0, "sec_name": "apple", "key1": "val1"},
        {"section_id": 2, "parent_sec_id": 0, "sec_name": "banana", "key2": "val2"},
        {"section_id": 3, "parent_sec_id": 1, "sec_name": "orange", "key3": "val3"},
        {"section_id": 4, "parent_sec_id": 2, "sec_name": "guava", "key4": "val4"},
        {"section_id": 5, "parent_sec_id": 3, "sec_name": "grape", "key5": "val5"},
    ]

    data = group_sections(data=list_dicts, parent_id="parent_sec_id", section_id="section_id")
    write_config_file(filename='config', data=data, name_key='sec_name')

config file :

apple
{
    'key1': 'val1'
    orange
    {
        'key3': 'val3'
        grape
        {
            'key5': 'val5'
        }
    }
}
banana
{
    'key2': 'val2'
    guava
    {
        'key4': 'val4'
    }
}

Note: This is an iterative solution, not a recursive one.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM