递归遍历多维字典,维度未知

时间:2010-10-05 04:18:34

标签: python

我想创建一个函数来递归遍历多维字典,其中维度是未知的。

这是我到目前为止所提出的,但它似乎没有正常工作。这将打印出一些键/值两次,它们不是有序的。

def walk_dict(d):
    for k,v in d.items():
        if isinstance(v, dict):
            walk_dict(v)
        else:
            print "%s %s" % (k, 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',
                },
            },
        },
    }

8 个答案:

答案 0 :(得分:13)

我不确定你的最终目标是什么,但代码正在做它应该做的事情。您正在看到您认为重复项目的内容,因为有'first_name'这样的键/值组合:'b'都在'account'内,'billing_info'在'account'中。我不确定你要找的是什么订单,但字典是无序的,所以你打印它们的功能必须给它们一些订单,例如更换以下内容:

for k,v in d.items():

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

或者你需要一本有序的字典。您也可以像这样使用pprint模块从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'}

但是,我不完全确定你的最终目标是什么。此外,当值为词典时,您缺少键。我修改了你的代码以执行与pprint在下面的操作类似的事情:

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) 

为你的例子dict产生:

>>> 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

答案 1 :(得分:3)

>>> import json

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

答案 2 :(得分:2)

这会正确打印键值对。你能指出哪些数据会被重复。基于以上数据可能会混淆这些键:

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

是两个词典的一部分 - “帐户”和“billing_info”。所以他们会在输出中出现两次。

此外,如果您想要某种顺序,您的词典K,V应该打印使用有序词典

答案 3 :(得分:1)

作为Justin Peel mentionspprint.pprint可能会做你想要的。

我认为您的代码存在的问题是您应该在递归之前先打印密钥,即更改

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

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

虽然在任何情况下,除非你添加缩进等,否则它看起来会很混乱。

这种事实际上非常复杂;如果您想获得一些想法,请查看pprint的代码。

答案 4 :(得分:1)

以下是Justin Peel接受的答案的变体,它将结果作为OrderedDict返回,而不是打印结果。

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

答案 5 :(得分:0)

你的代码完美无缺,它完全按照你所说的去做。

我在输出中看到的唯一重复是first_name和last_name,它们实际上定义了两次(在不同的字典中)。

关于'乱序'它是一本字典,它没有保证的顺序。也许它确实如此,但它将基于内部表示,你不应该依赖它。

您需要做的是按照您希望的方式对数据进行排序。您还可能希望打印出子字典的键值,以使输出更容易理解。

答案 6 :(得分:0)

在Python中,字典由键索引。键可以是任何不可变类型,如字符串或数字。除非键已排序,否则它们将始终以任意顺序返回。由于这个事实,您的walk_dict正在打印看似随机的结果。

以下是walk_dict打印所有键和值的示例。我已经在字典的每个级别对它们进行了排序。另外,我打印了每把钥匙。您的代码在递归之前没有打印密钥。最后,我添加了字符串填充以强调字典的每个级别。医生们都在路过。我希望这可以帮助你建立你的最终功能。

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()

答案 7 :(得分:0)

到目前为止,提到的解决方案并不能满足我的要求,但是使用贾斯汀·皮尔(Justin Peel)的拼写版本工作时,我能够以自己想要的方式打印出一些东西:

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 + '"'

其中印有以下内容:

"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