如何在python中将元组更改为字典树

时间:2014-02-21 08:55:35

标签: python dictionary

有一个像这样的元组:

t = (
  (1, -1, 'python'),
  (2, -1, 'ruby'),
  (3, -1, 'php'),
  (4, -1, 'lisp'),
  (5,  1, 'flask'),
  (6,  1, 'django'),
  (7,  1, 'webpy'),
  (8,  2, 'rails'),
  (9,  3, 'zend'),
  (10, 6, 'dblog')
)

我想将其更改为列表,每个列表项都是字典。这本词典有一个关键的儿子是另一本字典。如何更改元组t,我希望成为这样的列表字典树:

l = [
{
    'id': 1,
    'fid': -1,
    'title': 'python',
    'son': [
        {
            'id': 5,
            'fid': 1,
            'title': 'flask',
        },
        {
            'id': 6,
            'fid': 1,
            'title': 'django',
            'son': [
                {
                    'id': 10,
                    'fid': 6,
                    'title': 'dblog',
                },
            ]
        },
        {
            'id': 7,
            'fid': 1,
            'title': 'webpy',
        },
    ]
},
{
    'id': 2,
    'fid': -1,
    'title': 'ruby',
    'son': [
        {
            'id': 8,
            'fid': 2,
            'title': 'rails',
        },
    ]
},
{
    'id': 3,
    'fid': -1,
    'title': 'php',
    'son': [
        {
            'id': 9,
            'fid': 3,
            'title': 'zend',
        },
    ]
},
{
    'id': 4,
    'fid': -1,
    'title': 'lisp',
}

非常感谢

3 个答案:

答案 0 :(得分:1)

l = []
entries = {}

for id, fid, title in t:
    entries[id] = entry = {'id': id, 'fid': fid, 'title': title}
    if fid == -1:
        l.append(entry)
    else:
        parent = entries[fid]
        parent.setdefault('son', []).append(entry)

此处entries会跟踪到目前为止已创建的所有条目,以便您可以直接向其添加“子”,而无需搜索树。

这假定您的列表t已按id正确排序,并且儿子只是较低ID的子项。

演示:

>>> from pprint import pprint
>>> t = (
...   (1, -1, 'python'),
...   (2, -1, 'ruby'),
...   (3, -1, 'php'),
...   (4, -1, 'lisp'),
...   (5,  1, 'flask'),
...   (6,  1, 'django'),
...   (7,  1, 'webpy'),
...   (8,  2, 'rails'),
...   (9,  3, 'zend'),
...   (10, 6, 'dblog')
... )
>>> l = []
>>> entries = {}
>>> for id, fid, title in t:
...     entries[id] = entry = {'id': id, 'fid': fid, 'title': title}
...     if fid == -1:
...         l.append(entry)
...     else:
...         parent = entries[fid]
...         parent.setdefault('son', []).append(entry)
... 
>>> pprint(l)
[{'fid': -1,
  'id': 1,
  'son': [{'fid': 1, 'id': 5, 'title': 'flask'},
          {'fid': 1,
           'id': 6,
           'son': [{'fid': 6, 'id': 10, 'title': 'dblog'}],
           'title': 'django'},
          {'fid': 1, 'id': 7, 'title': 'webpy'}],
  'title': 'python'},
 {'fid': -1,
  'id': 2,
  'son': [{'fid': 2, 'id': 8, 'title': 'rails'}],
  'title': 'ruby'},
 {'fid': -1,
  'id': 3,
  'son': [{'fid': 3, 'id': 9, 'title': 'zend'}],
  'title': 'php'},
 {'fid': -1, 'id': 4, 'title': 'lisp'}]

如果“fid-after-after-id”假设不成立,则需要添加一个子ID队列,以便进行处理:

l = []
entries = {}
queue = {}

for id, fid, title in t:
    entries[id] = entry = {'id': id, 'fid': fid, 'title': title}
    if id in queue:
        entry['sons'] = queue[id]
        del queue[id]
    if fid == -1:
        l.append(entry)
    elif fid in entries:
        parent = entries[fid]
        parent.setdefault('son', []).append(entry)
    else:
        queue.setdefault(fid, []).append(entry)

if queue:
    raise ValueError('No entries found for fid(s) {}'.format(queue.keys()))

现在t中的条目顺序可以完全随机:

>>> import random
>>> t = list(t)
>>> random.shuffle(t)
>>> l = []
>>> entries = {}
>>> queue = {}
>>> for id, fid, title in t:
...     entries[id] = entry = {'id': id, 'fid': fid, 'title': title}
...     if id in queue:
...         entry['sons'] = queue[id]
...         del queue[id]
...     if fid == -1:
...         l.append(entry)
...     elif fid in entries:
...         parent = entries[fid]
...         parent.setdefault('son', []).append(entry)
...     else:
...         queue.setdefault(fid, []).append(entry)
... 
>>> if queue:
...     raise ValueError('No entries found for fid(s) {}'.format(queue.keys()))
... 
>>> pprint(l)
[{'fid': -1,
  'id': 1,
  'son': [{'fid': 1, 'id': 7, 'title': 'webpy'},
          {'fid': 1, 'id': 5, 'title': 'flask'},
          {'fid': 1,
           'id': 6,
           'sons': [{'fid': 6, 'id': 10, 'title': 'dblog'}],
           'title': 'django'}],
  'title': 'python'},
 {'fid': -1,
  'id': 2,
  'son': [{'fid': 2, 'id': 8, 'title': 'rails'}],
  'title': 'ruby'},
 {'fid': -1,
  'id': 3,
  'son': [{'fid': 3, 'id': 9, 'title': 'zend'}],
  'title': 'php'},
 {'fid': -1, 'id': 4, 'title': 'lisp'}]

fid可以是任意id,只要该ID在序列t中名为某处,或者对于根条目名称为-1

答案 1 :(得分:0)

创建一个单独的字典,将ID映射到本地id-fid-title-son字典。在单独的字典中找到fid后,将本地字典拼接在一起。如果本地字典的fid为-1,则将其添加到列表中。这是有效的,因为无论包含什么内容,本地字典都是同一个对象。

答案 2 :(得分:0)

如果初始列表已正确订购,请查看Martijn Pieters解决方案。但是,如果您无法保证订单,那么其中一个解决方案是在初始列表上循环两次:

children = {}
objs = []
l = []
for id, parent, title in t:
    obj = {
        "id": id,
        "fid": parent,
        "title": title
    }
    objs.append(obj)

    if parent == -1:  # keep only roots
        l.append(obj)

    if parent not in children:  # append to children
        children[parent] = []
    children[parent].append(obj)

for obj in objs:
    if obj["id"] in children:
        obj["son"] = children[obj["id"]]