Search code examples
pythondictionarypysidedefaultdict

convert a list of delimited strings to a tree/nested dict, using python


I am trying to convert a list of dot-separated strings, e.g.

['one.two.three.four', 'one.six.seven.eight', 'five.nine.ten', 'twelve.zero']

into a tree (nested lists or dicts - anything that is easy to walk through). The real data happens to have 1 to 4 dot-separated parts of different length and has 2200 records in total. My actual goal is to fill in the set of 4 QComboBox'es with this data, in manner that the 1st QComboBox is filled with first set items ['one', 'five', 'twelve'] (no duplicates). Then depending on the chosen item, the 2nd QComboBox is filled with its related items: for 'one' it would be: ['two', 'six'], and so on, if there's another nested level.

So far I've got a working list -> nested dicts solution, but it's horribly slow, since I use regular dict(). And I seem to have a trouble to redesign it to a defaultdict in a way to easily work out filling the ComboBoxes properly.

My current code:

def list2tree(m):
    tmp = {}
    for i in range(len(m)):
        if m.count('.') == 0:
            return m
        a = m.split('.', 1)
        try:
            tmp[a[0]].append(list2tree(a[1]))
        except (KeyError, AttributeError):
            tmp[a[0]] = list2tree(a[1])
    return tmp

main_dict = {}
i = 0
for m in methods:
    main_dict = list2tree(m)
    i += 1
    if (i % 100) == 0: print i, len(methods)
print main_dict, i, len(methods)

Solution

  • ls = ['one.two.three.four', 'one.six.seven.eight', 'five.nine.ten', 'twelve.zero']
    tree = {}
    
    for item in ls:
        t = tree
        for part in item.split('.'):
            t = t.setdefault(part, {})
    

    Result:

    {
     "twelve": {
      "zero": {}
     }, 
     "five": {
      "nine": {
       "ten": {}
      }
     }, 
     "one": {
      "six": {
       "seven": {
        "eight": {}
       }
      }, 
      "two": {
       "three": {
        "four": {}
       }
      }
     }
    }