2011-10-17 97 views
5

我有一个存储URL的字典列表。它只有两个字段,titleurl。例如:将列表中的一组URL作为树结构来表示

[ 
    {'title': 'Index Page', 'url': 'http://www.example.com/something/index.htm'}, 
    {'title': 'Other Page', 'url': 'http://www.example.com/something/other.htm'}, 
    {'title': 'About Page', 'url': 'http://www.example.com/thatthing/about.htm'}, 
    {'title': 'Detail Page', 'url': 'http://www.example.com/something/thisthing/detail.htm'}, 
] 

但是,我会从这个列表中得到一个树结构。我在寻找这样的事情:

{ 'www.example.com': 
    [ 
    { 'something': 
     [ 
     { 'thisthing': 
      [ 
      { 'title': 'Detail Page', 'url': 'detail.htm'} 
      ] 
     }, 
     [ 
      { 'title': 'Index Page', 'url': 'index.htm'}, 
      { 'title': 'Other Page', 'url': 'other.htm'} 
     ] 
     ] 
    }, 
    { 'thatthing': 
     [ 
     { 'title': 'About Page', 'url': 'about.htm'} 
     ] 
    } 
    ] 
} 

我在第一次尝试将是一堆的环的汤里urlparse,我相信有一个更好更快的方式来做到这一点。

我已经看到人们在SO工作魔术与列表解析,lambda函数等我仍然在找出它的过程。

(对于Django开发:我将使用这个我的Django应用我存储在一个名为Page模型,它有两个字段nametitle的URL。)

回答

3

第三次是魅力。这是你在那里的一些不错的结构:)。在你的评论中,你提到你“还没有能够想象更好的树格式来表示这样的数据” ......这让我再次冒昧(稍微)改变输出的格式。为了动态添加子元素,必须创建一个字典来容纳它们。但是对于“叶节点”,这个字典永远不会被填充。如果需要,它们当然可以通过另一个循环来移除,但在迭代过程中不会发生,因为对于可能的新节点应该存在空的dict。有些节点在没有文件的情况下:这些节点将包含一个空的list

ll = [ 
    {'title': 'Index Page', 'url': 'http://www.example.com/something/index.htm'}, 
    {'title': 'Other Page', 'url': 'http://www.example.com/something/other.htm'}, 
    {'title': 'About Page', 'url': 'http://www.example.com/thatthing/about.htm'}, 
    {'title': 'Detail Page', 'url': 'http://www.example.com/something/thisthing/detail.htm'}, 
] 

# First build a list of all url segments: final item is the title/url dict 
paths = [] 
for item in ll: 
    split = item['url'].split('/') 
    paths.append(split[2:-1]) 
    paths[-1].append({'title': item['title'], 'url': split[-1]}) 

# Loop over these paths, building the format as we go along 
root = {} 
for path in paths: 
    branch = root.setdefault(path[0], [{}, []]) 
    for step in path[1:-1]: 
     branch = branch[0].setdefault(step, [{}, []]) 
    branch[1].append(path[-1]) 

# As for the cleanup: because of the alternating lists and 
# dicts it is a bit more complex, but the following works: 
def walker(coll): 
    if isinstance(coll, list): 
     for item in coll: 
      yield item 
    if isinstance(coll, dict): 
     for item in coll.itervalues(): 
      yield item 

def deleter(coll): 
    for data in walker(coll): 
     if data == [] or data == {}: 
      coll.remove(data) 
     deleter(data) 

deleter(root) 

import pprint 
pprint.pprint(root) 

输出:

{'www.example.com': 
    [ 
     {'something': 
      [ 
       {'thisthing': 
        [ 
         [ 
          {'title': 'Detail Page', 'url': 'detail.htm'} 
         ] 
        ] 
       }, 
       [ 
        {'title': 'Index Page', 'url': 'index.htm'}, 
        {'title': 'Other Page', 'url': 'other.htm'} 
       ] 
      ], 
     'thatthing': 
      [ 
       [ 
        {'title': 'About Page', 'url': 'about.htm'} 
       ] 
      ] 
     }, 
    ] 
} 
+0

这似乎只适用于一层深的路径。我应该更加明确。它不适用于像这样的URL:http:// www.example.com/thisthing/thisthing/about.htm。 –

+0

嗨Jro。我无权改变这些模型,所以没有了。这样做的原因是通过JSON返回所有这些记录。你说得对,检查一个节点是否是一个列表来查看它是否是一组页面这一事实是丑陋的,但我没有想到用更好的树格式来表示这样的数据。我回到了尝试将该URL列表转换为示例数据格式的原始问题。我非常感谢你的帮助,但如果你能告诉我如何转换它,这将是一种解脱。我一直在打我的头,但没有运气。谢谢Jro。 –

+0

啊哈。谢谢。 Thaks你Jro。我已经接受了你的答案,但只有一件小事:我怎么能删除所有的空白字典和列表?我需要递归遍历整棵树吗? –

0

这里是我的解决方案。它似乎工作。从Jro的一个非常不同的方法:

import itertools 
import pprint 

pages = [ 
    {'title': 'Index Page', 'url': 'http://www.example.com/something/index.htm'}, 
    {'title': 'Other Page', 'url': 'http://www.example.com/something/other.htm'}, 
    {'title': 'About Page', 'url': 'http://www.example.com/thatthing/about.htm'}, 
    {'title': 'dtgtet Page', 'url': 'http://www.example.com/thatthing/'}, 
    {'title': 'Detail Page', 'url': 'http://www.example.com/something/thisthing/detail.htm'}, 
    {'title': 'Detail Page', 'url': 'http://www.example.com/something/thisthing/thisthing/detail.htm'}, 
] 



def group_urls(url_set, depth=0): 
    """ 
    Fetches the actions for a particular domain 
    """ 
    url_set = sorted(url_set, key=lambda x: x['url'][depth]) 

    tree = [] 

    leaves = filter(lambda x: len(x['url']) - 1 == depth, url_set) 
    for cluster, group in itertools.groupby(leaves, lambda x: x['url'][depth]): 
     branch = list(group) 
     tree.append({cluster: branch}) 

    twigs = filter(lambda x: len(x['url']) - 1 > depth, url_set) 
    for cluster, group in itertools.groupby(twigs, lambda x: x['url'][depth]): 
     branch = group_urls(list(group), depth+1) 
     tree.append({cluster: branch}) 

    return tree 

if __name__ == '__main__': 
    for page in pages: 
     page['url'] = page['url'].strip('http://').split('/') 

    pprint.pprint(group_urls(pages)) 

我似乎无法弄清楚为什么我需要排序在每次递归。我敢打赌,如果我在周围工作,我可以再刮几秒钟。