Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to loop over a dictionary with more than 3 sublevels of dictionaries inside it

I am trying to loop over a dictionary where some keys have other dictionaries as values and some of those values are a key for a dictionary it has as a value. I am parsing a YAML file with over 5000 lines using pyyaml. When I load it, it creates a dictionary of everything in the file and breaks all the sub levels into dictionaries of their own.

Here is what I am using now which gets me just the keys and values of things that are not dictionaries themselves but the keys and values of dictionaries:

for k1,v1 in dataMap['eng'].items():
    if type(dataMap['eng'][k1]) == dict:
        for k2, v2 in dataMap['eng'][k1].items():
            if type(dataMap['eng'][k1][k2]) == dict:
                for k3, v3 in dataMap['eng'][k1][k2].items():
                    if type(dataMap['eng'][k1][k2][k3]) == dict:
                        print "key: {}, val: {}".format(k3, v3)
                    else:
                        print "key: {}, val: {}".format(k3, v3)
            else:
                print "key: {}, val: {}".format(k2, v2)
    else:
        print "key: {}, val: {}".format(k1, v1)

Where 'eng' is the top most level that holds everything else within it in the YAML file. Below is a smple of the YAML file is am working with. The actual file has over 5000 lines in it though

eng:
  value1: 
    v1: USA
    v2: Canada
    v3: Mexico
  value2:
    value3:
       value4: "some text here"
       value5:
          value6: "some text here"
          value7: " some other text here"
       value8: "some text here"
    etc..

It goes on and on like this. Some keys just have values, some keys have dictionaries which themselves have dictionaries and this can go on a few levels deep. I am just wondering if there is a better and easier way to do this then they way I am handling it with all the for loops and if statements checking if its a dict or not. In the end I would just need something like this:

v1: USA
v2: Canada
v3: Mexico
value4: "some text here"
value6: "some text here"
value7: " some other text here"
value8: "some text here"
like image 623
Alex Y. Avatar asked Feb 17 '23 06:02

Alex Y.


1 Answers

Sounds like you want a solution using recursion:

def recurse( x ):
  for k, v in x.items():
    if isinstance( v , dict ):
      recurse( v )
    else:
      print "key: {}, val: {}".format(k, v) 

Note that recursion will result in a stack overflow if you go too deep. However, the limit is generally much higher than what you seem to be expecting here:

>>> import sys
>>> sys.getrecursionlimit()
1000
>>> def f(i):
      f(i+1)
>>> f(1)
...
File "<stdin>", line 3, in f
File "<stdin>", line 3, in f
File "<stdin>", line 3, in f
RuntimeError: maximum recursion depth exceeded

However, if you're writing a program where it would be a problem that someone can maliciously craft a file and make your parser crash (raise an exception), then you should consider converting the recursion to an iteration, which is always possible. The tradeoff is that the recursive code is very easy to read and write, and the iterative conversion sometimes slightly less so.

like image 126
svk Avatar answered Apr 26 '23 09:04

svk