我有一个存储网址的dicts列表。它只有两个字段title
和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'},
]
但是,我要从这个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
的模型中,该模型有两个字段name
和title
)< / p>
答案 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))
我似乎无法弄清楚为什么我需要在每次递归的开始时进行排序。我敢打赌,如果我在那里工作,我可以再刮几秒钟。