【问题标题】:How can I sort a nested list accordign to each element in nested lists如何根据嵌套列表中的每个元素对嵌套列表进行排序
【发布时间】:2017-02-08 05:33:29
【问题描述】:

我有一个模型页面,它存储一个可选的父页面(它自己的实例)。在我看来,我创建了一个函数,它将返回所有页面及其父页面的嵌套列表。

比如我的页面架构是

CCC
  AAA
    DDD
    KKK
      EEE
ZZZ
  BBB

所以 DDD 有父页面 AAA,它有自己的父页面 CCC。 CCC 是首页,没有父页面。

该函数将首先获取所有页面实例的查询集并按字母顺序对它们进行排序。然后它将继续递归地生成一个“完整的父架构”列表,其中该列表上的每个元素都是所有父页面的另一个列表,包括页面本身。从上面的示例中,如果我们为页面 DDD 获取一个列表切片,它将返回 [CCC, AAA, DDD]。

对于上述示例,我的函数当前返回这样的列表:

[ 
  [CCC, AAA],
  [ZZZ, BBB],
  [CCC],
  [CCC, AAA, DDD],
  [CCC, AAA, KKK, EEE],
  [CCC, AAA, KKK],
  [ZZZ],
]

从该列表中可以看出,所有元素都根据该列表中的最后一个元素按字母顺序排序。现在我想在我的前端显示所有这些父页面,基本上看起来像一个站点地图,并显示我网站上所有页面的正确父架构,这些页面根据每个嵌套列表中的每个元素按字母顺序排序。最终结果是:

[ 
  [CCC],
  [CCC, AAA],
  [CCC, AAA, DDD],
  [CCC, AAA, KKK],
  [CCC, AAA, KKK, EEE],
  [ZZZ],
  [ZZZ, BBB],
]

简单地说,我想遍历每个列表中的每个第一个元素并按字母顺序对其进行排序,然后是每个第二个元素并对其进行排序,然后是第三个,然后是第四个,依此类推。有没有办法做到这一点?

编辑: 为避免混淆,这是我的观点:

def page_list(request):


    # Fetch all pages and sort them alphabetically
    queryset = Page.objects.all().order_by("title")
    output = []

    # Generate list of pages and their parents
    for page in queryset:
        output.append(get_list_of_parents(page))

    context = {
        "title": "Page List",
        "page_list": output,
    }

    return render(request, template + '/page_list.html', context)

# Get an array of all parent instances of a Page model
def get_list_of_parents(page, list=None):

    current_page = page 
    parent_list = []    

    if list is not None:
        parent_list = list

    parent_list.append(current_page)

    if current_page.parent is not None:
        parent_list = get_list_of_parents(current_page.parent, parent_list)
    else:
        # if this is the last parent page, reverse the order of list to display list in form of parent path to child
        parent_list.reverse()

    return parent_list

【问题讨论】:

  • 最终结果是否有错误?你的意思是:[CCC, AAA, DDD], [CCC, AAA, KKK], [CCC, AAA, KKK, EEE],
  • 使用深度优先搜索算法遍历你的树,将每一步存储在一个列表中。
  • 您是在问如何按字母顺序对列表的子列表进行排序?
  • @LaurentLAPORTE 是的,这是一个错字,我会在一秒钟内编辑
  • @Mr.goosberry 不,每个嵌套列表都已正确排序。对于这些列表中的每一个,最后一个元素是最后一个子元素,所有之前的元素都是父元素,因此对于页面 DDD,它显示 [parentOfParenOfDDD, parentOfDDD, DDD]

标签: python django sorting nested-lists


【解决方案1】:

利用 itertools 中的辅助函数,使用简单高效的单行比较函数进行排序。

>>> import pprint
>>> from itertools import dropwhile, izip_longest
>>> pages = [['CCC', 'AAA'],
...          ['ZZZ', 'BBB'],
...          ['CCC'],
...          ['CCC', 'AAA', 'DDD'],
...          ['CCC', 'AAA', 'DDD', 'EEE'],
...          ['CCC', 'AAA', 'KKK'],
...          ['ZZZ']]
>>> pprint.pprint(sorted(pages, cmp=lambda a, b: cmp(*next(dropwhile(lambda x: not cmp(x[0], x[1]), izip_longest(a, b))))))
[['CCC'],
 ['CCC', 'AAA'],
 ['CCC', 'AAA', 'DDD'],
 ['CCC', 'AAA', 'DDD', 'EEE'],
 ['CCC', 'AAA', 'KKK'],
 ['ZZZ'],
 ['ZZZ', 'BBB']]
>>>

这不仅适用于字符串,也适用于 Page 实例,只要它们具有可比性,即您已在 Page 类中定义了 __cmp__ 方法。

【讨论】:

  • 谁对此投了反对票,请随时评论为什么这不是一个好的解决方案和/或提供更好的答案。谢谢。
  • 好的,现在开始测试。 Atm 我得到一个 typerror 'cmp' is an invalid keyword argument for this function,我猜这是页面模型中缺少__cmp__。让我看看能不能成功
  • 如果你能告诉我们你的 Page 模型是如何定义的,我可以告诉你如何定义你的 __cmp__ 方法。
  • 我刚刚在您的问题中看到了编辑,现在可以假设您的页面是通过它们的标题进行比较的,在这种情况下,您的 __cmp__ 可以定义为:def __cmp__(self, other): return cmp(self.title, other.title)
  • 我将def __cmp__(self, other): return cmp(self.title, other.title) 添加到模型中,尽管它们可以与id 或slug 进行比较。但是错误仍然存​​在:TypeError at /dashboard/pages/custom 'cmp' is an invalid keyword argument for this function在线test = sorted(output, cmp=lambda a, b: cmp(*next(dropwhile(lambda x: not cmp(x[0], x[1]), izip_longest(a, b)))))
【解决方案2】:

使用页面树:

我定义了这个简单的树形结构:

class Page(object):
    def __init__(self, name, children=None):
        self.name = name
        self.children = children or []

    def display(self, indent=""):
        child_repr = [child.display(indent=indent + "  ") for child in self.children]
        return indent + self.name + "\n" + "".join(child_repr)

    def __str__(self):
        return self.display()

display__str__ 方法用于打印。

我可以将您的树构建为页面列表:

tree = [
    Page("CCC", [
        Page("AAA", [
            Page("DDD"),
            Page("KKK", [
                Page("EEE")])])]),
    Page("ZZZ", [
        Page("BBB")])]

我可以这样显示结构:

for item in tree:
    print(item)

我明白了:

CCC
  AAA
    DDD
    KKK
      EEE

ZZZ
  BBB

为了遍历树,我定义了以下方法:

    def traverse(self, result, stack=None):
        stack = stack or []
        stack.append(self.name)
        result.append(list(stack))
        for child in self.children:
            child.traverse(result, stack)
        stack.pop()

result 是要填写的列表。

用法:

result = []
for item in tree:
    item.traverse(result)

import pprint

pprint.pprint(result)

我会得到:

[['CCC'],
 ['CCC', 'AAA'],
 ['CCC', 'AAA', 'DDD'],
 ['CCC', 'AAA', 'KKK'],
 ['CCC', 'AAA', 'KKK', 'EEE'],
 ['ZZZ'],
 ['ZZZ', 'BBB']]

太棒了!

【讨论】:

  • 我对 python 和 django 还很陌生,但据我了解,您建议我更改页面模型,以便它存储子列表而不是单亲?如果是这样,那不是一个选择。发布者必须保存一个页面,然后编辑其他页面以添加新页面作为子页面。问题是,如果发布者不是父页面的所有者,他们将无法对其进行编辑。只有创建该页面的发布者与之关联,并且只有他们可以编辑/删除该页面。其他发布者可以查看,并将其设置为父页面,但如果他们不是原始发布者,则不能更改父实例
  • PS,既然你回复了,我更新了我的原始帖子,并将这两个函数包含在该列表中的所有页面中,并创建所有页面及其父级的嵌套列表。
  • 我不知道您的数据库模型,但通常,当您有父/子关系时,父级维护一个子级列表,每个子级都有对其父级的引用。由 ORM 为您处理这种关系。所以,通常这不是问题。
猜你喜欢
  • 1970-01-01
  • 2017-07-09
  • 1970-01-01
  • 1970-01-01
  • 2013-02-06
  • 2018-02-25
  • 1970-01-01
相关资源
最近更新 更多