将列表中的一组URL表示为树结构

时间:2011-10-17 13:30:07

标签: python

我有一个存储网址的dicts列表。它只有两个字段titleurl。例如:

[
  {'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'},
]

但是,我要从这个dicts列表中获取树结构。我正在寻找这样的东西:

{ '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'}
      ]
    }
  ]
}

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

我已经看到人们在SO工作魔法上有列表推导,lambda函数等等。我仍然在弄清楚它。

(对于Django开发人员:我将使用这个我的Django应用程序。我将URL存储在名为Page的模型中,该模型有两个字段nametitle)< / p>

2 个答案:

答案 0 :(得分: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'}
                ]
            ]
        },
    ]
}

答案 1 :(得分: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))

我似乎无法弄清楚为什么我需要在每次递归的开始时进行排序。我敢打赌,如果我在那里工作,我可以再刮几秒钟。