循环嵌套类别的更好方法是什么?

时间:2016-09-29 04:08:41

标签: python

我有一个sqlalchemy类别表,如下所示:

id | parent_id | name
1 | 0 | license
2 | 1 | Digital Media
3 | 1 | Advertising
4 | 2 | Email Marketing

我的目标是将其转换为嵌套字典或列表,我可以在Flask模板中循环。下面的代码有效但对我来说看起来很混乱。有没有办法可以改进它?

categories = Category.query.all()
roots = [root for root in categories if root.parent_id == 1]
items = []
for root in roots:
    items.append({'root': root.name, 'subcategories': []})
    for category in categories:
        if category.parent_id == root.id:
            items[-1]['subcategories'].append(category.name) 

2 个答案:

答案 0 :(得分:1)

如果您只想更简洁地编写您提出的逻辑,请查看以下内容:

categories = Category.query.all()
items = [{'root': root.name, 'subcategories':
             [category.name for category in categories if category.parent_id == root.id]
         }
    for root in categories if root.parent_id == 1]

请注意,这在O(N 2 )中运行,其中N是类别数。如果您希望更好地执行此操作,则可以将parent_id映射到类别。例如:

categories = Category.query.all()
category_by_parent_id = {}
for category in categories:
    if category.parent_id not in category_by_parent_id:
        category_by_parent_id[category.parent_id] = [category]
    else:
        category_by_parent_id[category.parent_id].append(category)
items = [{'root': root.name, 'subcategories': category_by_parent_id[root.id] if root.id in category_by_parent_id else []} for root in category_by_parent_id[1]]

上述工作是将每个parent_id映射设置为具有该父ID的类别列表。然后我们循环遍历所有根类别(字典中parent_id 1的列表),并将子类别设置为等于根id的类别列表。如果root的id不在字典中,我们会将子类别设置为空列表。现在这个操作的运行时是O(N),因为我们只运行列表两次,而O(2N)= O(N)。

使用集合中的默认字典可以更好地完成此方法。 default_dict需要指定默认值,并且假定当前不在字典中的所有键都具有该默认值。例如,a = default_dict(0); print(a[1])将打印0,因为这是为列表指定的默认值,而键1当前不存在。这可以用来简化上面的方法:

from collection import default_dict

categories = Category.query.all()
category_by_parent_id = default_dict([])
for category in categories:
    category_by_parent_id[category.parent_id].append(category)
items = [{'root': root.name, 'subcategories': category_by_parent_id[root.id]} for root in category_by_parent_id[1]]

答案 1 :(得分:0)

您可以嵌套列表推导,因此您无需在rootscategories内进行双循环。

categories = Category.query.all()
roots = [root for root in categories if root.parent_id == 1]
items = [
    {
        'root': root.name,
        'subcategories': [
            category.name
            for category in categories
            if category.parent_id == root.id
        ]
    }
    for root in roots
]