开发者

recursively traverse multidimensional dictionary, dimension unknown

I want to create a function to recursively traverse a multidimensional dictionary, where the dimensions are unknown.

Here is what I have come up with so far, but it doesn't seem to be working correctly. This will print out some key / values twice and they are not in order.

def walk_dict(d):
    for k,v in d.items():
        if isinstance(v, dict):
            walk_dict(v)
        else:
            print "%s %s" % (k, v) 

Here's a sample array:

d = {
        'plan_code': 'b',
        'quantity': '1',
        'account': {
            'account_code': 'b',
            'username': 'jdoe',
            'email': 'jdoe@domain.com',
            'first_name': 'b',
            'last_name': 'b',
            'company_name': 'Company, LLC.',
            'billing_info': {
                'first_name': 'b',
                'last_name': 'b',
                'address1': '123 Test St',
                'city': 'San Francisco',
                'state': 'CA',
                'country': 'US',
                'zip': '94105',
     开发者_Python百科           'credit_card': {
                    'number': '1',
                    'year': '2018',
                    'month': '12',
                    'verification_value': '123',
                },
            },
        },
    }


I'm not sure what your ultimate goal is, but the code is doing what it is supposed to. You are seeing what you think are repeats of items because there are key/value combos like 'first_name':'b' that are both within 'account' and within 'billing_info' within 'account'. I'm not sure what order you are looking for, but dictionaries are unordered so your function to print them out will have to give them some order, for instance by replacing the following:

for k,v in d.items():

with

for k,v in sorted(d.items(),key=lambda x: x[0]):

or you'll need an ordered dictionary. You can also use the pprint module like so to give a nice print out of a dict:

>>> import pprint
>>> pprint.pprint(d)
{'account': {'account_code': 'b',
             'billing_info': {'address1': '123 Test St',
                              'city': 'San Francisco',
                              'country': 'US',
                              'credit_card': {'month': '12',
                                              'number': '1',
                                              'verification_value': '123',
                                              'year': '2018'},
                              'first_name': 'b',
                              'last_name': 'b',
                              'state': 'CA',
                              'zip': '94105'},
             'company_name': 'Company, LLC.',
             'email': 'jdoe@domain.com',
             'first_name': 'b',
             'last_name': 'b',
             'username': 'jdoe'},
 'plan_code': 'b',
 'quantity': '1'}

However, I'm not fully sure what your end goal is here. Also, you are missing the keys when the values are dictionaries. I modified your code to do a similar thing to what pprint does in the following:

def walk_dict(d,depth=0):
    for k,v in sorted(d.items(),key=lambda x: x[0]):
        if isinstance(v, dict):
            print ("  ")*depth + ("%s" % k)
            walk_dict(v,depth+1)
        else:
            print ("  ")*depth + "%s %s" % (k, v) 

which for your example dict yields:

>>> walk_dict(d)
account
  account_code b
  billing_info
    address1 123 Test St
    city San Francisco
    country US
    credit_card
      month 12
      number 1
      verification_value 123
      year 2018
    first_name b
    last_name b
    state CA
    zip 94105
  company_name Company, LLC.
  email jdoe@domain.com
  first_name b
  last_name b
  username jdoe
plan_code b
quantity 1


import json
print(json.dumps(d, indent=4))


This does print the key, value pairs correctly. Can you point out which data gets repeated. There can be confusion based on the above data as these keys :

'first_name': 'b',
'last_name': 'b',

are part of two dictionaries - 'account' and 'billing_info'. So they will appear twice in output.

Also if you want some kind of order in which your dictionaries K,V should get printed use Ordered Dictionaries

  • http://docs.python.org/dev/library/collections.html#collections.OrderedDict
  • Key-ordered dict in Python
  • http://code.activestate.com/recipes/496761-a-more-clean-implementation-for-ordered-dictionary/


Here is a variation on the accepted answer by Justin Peel that returns its result as an OrderedDict instead of printing the result.

from collections import OrderedDict

def sort_by_keys(dct,):
    new_dct = OrderedDict({})
    for item in sorted(dct.items(), key=lambda (key, val): key):
        key = item[0]
        val = item[1]
        if isinstance(val, dict):
            new_dct[key] = sort_by_keys(val)
        else:
            new_dct[key] = val
    return new_dct


As Justin Peel mentions, pprint.pprint will probably do what you want.

I think the problem with your code is that you should be printing the key first before recursing, i.e. change

    if isinstance(v, dict):
        walk_dict(v)

to

    if isinstance(v, dict):
        print k
        walk_dict(v)

Though in any case it's going to look pretty confusing unless you add indentation and such.

This sort of thing is actually pretty complicated; check out the code for pprint if you want to get some ideas.


your code works perfect fine, it does exactly what you told it to.

The only repeats i see in the output are first_name and last_name which are in fact defined twice (in different dictionaries).

As to 'out of order' Its a dictionary, it doesn't have a guaranteed order. well maybe it does, but it would be based on the internal representation, which you shouldn't rely on.

What you need to do is sort the data in the way that you want it to come out. You also may want to print out the sub dictionary's key value to make the output easier to understand.


In Python, dictionaries are indexed by keys. Keys can be any immutable type like a string or number. Unless the keys are sorted, they will always be returned in an arbitrary order. Because of this fact, your walk_dict is printing seemingly random results.

Here is an example of walk_dict which prints all keys and values. I've sorted they keys at each level of the dictionary. Also, I printed each key. Your code didn't print a key before it recursed. Finally, I've added string padding to emphasize each level of the dictionary. The doctests are all passing. I hope this helps you build your final function.

import doctest


def walk_dict(seq, level=0):
    """Recursively traverse a multidimensional dictionary and print all
    keys and values.

    >>> d = {'dog': 'dusty', 'cat': 'fluffy', 'bird': 'chirpy'}
    >>> walk_dict(d)
    bird chirpy
    cat fluffy
    dog dusty
    >>> d = {'location': 'home', 'animals':{'dog': 'dusty', 'cat': 'fluffy', 'bird': 'chirpy'}}
    >>> walk_dict(d)
    animals
      bird chirpy
      cat fluffy
      dog dusty
    location home
    >>> d = {'location': 'home', 'animals':{'dog': 'dusty', 'cat': 'fluffy', 'bird': {'name':'chirpy', 'color':'blue'}}}
    >>> walk_dict(d)
    animals
      bird
        color blue
        name chirpy
      cat fluffy
      dog dusty
    location home
    >>> d = { \
            'plan_code': 'b', \
            'quantity': '1', \
            'account': { \
                'account_code': 'b', \
                'username': 'jdoe', \
                'email': 'jdoe@domain.com', \
                'first_name': 'b', \
                'last_name': 'b', \
                'company_name': 'Company, LLC.', \
                'billing_info': { \
                    'first_name': 'b', \
                    'last_name': 'b', \
                    'address1': '123 Test St', \
                    'city': 'San Francisco', \
                    'state': 'CA', \
                    'country': 'US', \
                    'zip': '94105', \
                    'credit_card': { \
                        'number': '1', \
                        'year': '2018', \
                        'month': '12', \
                        'verification_value': '123', \
                    }, \
                }, \
            }, \
        } 
    >>> walk_dict(d)
    account
      account_code b
      billing_info
        address1 123 Test St
        city San Francisco
        country US
        credit_card
          month 12
          number 1
          verification_value 123
          year 2018
        first_name b
        last_name b
        state CA
        zip 94105
      company_name Company, LLC.
      email jdoe@domain.com
      first_name b
      last_name b
      username jdoe
    plan_code b
    quantity 1
    """
    items = seq.items()
    items.sort()
    for v in items:
        if isinstance(v[1], dict):
            # Print the key before make a recursive call
            print "%s%s" % ("  " * level, v[0])
            nextlevel = level + 1
            walk_dict(v[1], nextlevel)
        else:
            print "%s%s %s" % ("  " * level, v[0], v[1])


if __name__ == '__main__':
    doctest.testmod()


The solutions mentioned so far didn't do what I wanted but working from Justin Peel's spelled out version, I was able to make something that printed in a way I wanted:

def dict_parser(data, depth=0):
    if isinstance(data, dict):
        for key in data:
            value = data[key]
            print('  ') * depth + '"' + key + '":'
            dict_parser(value, depth + 1)
    elif isinstance(data, list):
        for item in data:
            if isinstance(item, list):
                dict_parser(item, depth + 1)
            else:
                print('  ') * depth + '"' + item + '"'
    elif isinstance(data, bool):
        print('  ') * depth + str(data)
    else:
        print('  ') * depth + '"' + data + '"'

Which printed something like:

"saved_skels":
  "E:\Child_Skeleton_6ft.fbx"
  "E:\Female_Skeleton_6ft.fbx"
  "E:\HeroFemale_Skeleton.fbx"
  "E:\HeroMale_Skeleton.fbx"
"saved_roots":
  "E:\users\Characters"
  "E:\content\characters"
"saved_tabs":
  "Characters":
    "ProxyFemale":
      "HeroFemale_Skeleton.fbx"
    "animation":
      "HeroFemale_Skeleton.fbx"
    "ProxyMale":
      "HeroMale_Skeleton.fbx"
  "Game":
    ""
  "Proto":
    "Female":
      "Female_Skeleton_NewBP.fbx"
  "Assets":
    ""
  "characters":
    "player":
      "player_Skeleton.fbx"
    "actor":
      "Hero_Skeleton.fbx"
"edit_common_checkbox_state":
  False
"get_latest_checkbox_state":
  True


Here is an instance that also converts arbitrary objects into dictionaries (if Python can):

from typing import Union

def walk_dict(d, depth=0) -> None:
    for k, v in sorted(d.items(), key=lambda x: x[0]):
        indentation = depth * "  "
        if not isinstance(v, Union[int, list, None, str]):
            if isinstance(v, dict):
                print(f"k={k}")
                walk_dict(v, depth + 1)
            elif isinstance(v.__dict__, dict):
                print(f"k={k}")
                walk_dict(v.__dict__, depth + 1)
        else:
            print(f"{indentation}k={k},  v={v}")


d = {
    "plan_code": "b",
    "quantity": "1",
    "account": {
        "account_code": "b",
        "username": "jdoe",
        "email": "jdoe@domain.com",
        "first_name": "b",
        "last_name": "b",
        "company_name": "Company, LLC.",
        "billing_info": {
            "first_name": "b",
            "last_name": "b",
            "address1": "123 Test St",
            "city": "San Francisco",
            "state": "CA",
            "country": "US",
            "zip": "94105",
            "credit_card": {
                "number": "1",
                "year": "2018",
                "month": "12",
                "verification_value": "123",
            },
        },
    },
}


walk_dict(d)

Outputs:

k=account
  k=account_code,  v=b
k=billing_info
    k=address1,  v=123 Test St
    k=city,  v=San Francisco
    k=country,  v=US
k=credit_card
      k=month,  v=12
      k=number,  v=1
      k=verification_value,  v=123
      k=year,  v=2018
    k=first_name,  v=b
    k=last_name,  v=b
    k=state,  v=CA
    k=zip,  v=94105
  k=company_name,  v=Company, LLC.
  k=email,  v=jdoe@domain.com
  k=first_name,  v=b
  k=last_name,  v=b
  k=username,  v=jdoe
k=plan_code,  v=b
k=quantity,  v=1
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜