Create multi-level dictionary from dash-separated string (unknown how deep)

For a "heath-check" project, I'm trying to create a multi-dimension array from strings that are written in a text file.

Currently I have it working with single variables like this:

someParameter1 = someValue1 # With some comments
someParameter2 = someValue2 # With some other comments

This will be translated into a dictionary:

ini['someParameter1'] = 'someValue1'
ini['someParameter2'] = 'someValue2'

Now I just added checks on tables and therefor I use two-dimensional array's, build from dash-separated strings like this:

someTable-someParameter1 = someValue1 # With some comments
someTable-someParameter2 = someValue2 # With some other comments

This will be translated into a two-level dictionary:

ini['someTable']['someParameter1'] = 'someValue1'
ini['someTable']['someParameter2'] = 'someValue2'

For this I hard-coded a limit on 2 fields before the '=' sign:

# In case the key contains a dash, create a 2-dimensional key
for inikey in list(ini):  # loop thru the ini to find key's with a dash
    if '-' in inikey:
        part1,part2 = inikey.split('-',1)    # <= Only split on the first dash
        try:
            ini[part1][part2] = ini[inikey]
        except KeyError:  # In case ini[part1] does not exist yet
            ini[part1] = {}
            ini[part1][part2] = ini[inikey]

Because of this, when I have in my config file: aaa-bbb-ccc-ddd = xyz, this will be translated to ini['aaa']['bbb-ccc-ddd'] = 'xyz'

Now I look for a nice trick to create ini['aaa']['bbb]['ccc']['ddd'] = 'xyz' without writing a piece of code for every expected amount of levels. So if possible also with 3, 4, or whatever number of words before the =, creating a list with unknown amount of dimensions, as deep as needed.

Upvotes: 2

Views: 171

Answers (2)

blhsing
blhsing

Reputation: 106891

You can split the key by dashes into parent keys and a final key, then iterate through the parent keys to build the sub-dicts if a parent key is missing, and finally assign the current value to the final key of the current node for each line of input:

from io import StringIO

file = StringIO('''someParameter1 = someValue1
someParameter2 = someValue2
someTable-someParameter1 = someValue1
someTable-someParameter2 = someValue2
aaa-bbb-ccc-ddd = xyz''')

ini = {}
for line in file:
    node = ini
    key, value = line.rstrip().split(' = ')
    *parents, key = key.split('-')
    for parent in parents:
        node[parent] = node = node.get(parent, {})
    node[key] = value

ini would become:

{'someParameter1': 'someValue1',
 'someParameter2': 'someValue2',
 'someTable': {'someParameter1': 'someValue1', 'someParameter2': 'someValue2'},
 'aaa': {'bbb': {'ccc': {'ddd': 'xyz'}}}}

Demo: https://repl.it/@blhsing/ForkedSufficientNumericalanalysis

Upvotes: 1

Open AI - Opting Out
Open AI - Opting Out

Reputation: 24153

You can use defaultdict and create it with a default dict. If you call that a Tree, we have a recursive data structure:

from collections import defaultdict

def Tree():
    return defaultdict(Tree)

This will create new sub trees every time a new key is used:

>>> tree = Tree()
>>> tree['a']['b']['c'] = 123

If we have a list of keys, we can loop through them keeping track of each sub-tree until we've use all but the last key. Then we set the value for the last key in the last sub-tree:

ini = Tree()

config_line = 'aaa-bbb-ccc-ddd = xyz'
keys, value = config_line.split(' = ')

keys = keys.split('-')

subtree = ini
for key in keys[:-1]:
    subtree = subtree[key]

subtree[keys[-1]] = value

Upvotes: 2

Related Questions