【问题标题】:Recursive sorting function for list in PythonPython中列表的递归排序函数
【发布时间】:2013-09-14 20:54:22
【问题描述】:

我想要一个如下列表:

groups = ["foo", "bar", "foo::fone", "foo::ftwo", "foo::ftwo::ffone"]

并将其转换为嵌套列表,可能格式如下,但我愿意接受建议:

groups_sorted = [{
                    "name":"foo",
                    "children": [
                                  {
                                    "name": "foo::fone",
                                    "children": [ ... ]
                                  }, ...
                                ]
                 }, ...
                ]

因此列表使用:: 上的层次结构拆分进行排序。我需要将每个 children 键作为列表本身,因为列表的原始顺序很重要。

我已经玩了几个小时,并且能够从单个顶部节点开始创建递归字典,但我做不到最后一点。在下面找到我的工作:

def children_of(node, candidates):
    children = []
    remainder = []
    for c in candidates:
        sub = node + "::"
        if c.startswith(sub):
            try:
                c[len(sub):].index("::") # any more separators = not a child
                remainder.append(c)
            except ValueError: # a child
                children.append(c)    
        else: #not related
            remainder.append(c)
    return children, remainder

def sortit(l):
    if l:
        el = l.pop(0)
        children, remainder = children_of(el,l)
        if children:    
            return { "name": el,
                     "children": [sortit([c]+remainder) for c in children]
                   }
        else:
            return { "name": el }

编辑:@Thijs van Dien 的解决方案非常好,但我需要 2.6 兼容性,这使我无法使用 OrderDicts。

【问题讨论】:

    标签: python recursion nested-loops python-2.6 nested-lists


    【解决方案1】:

    换成这样的怎么样?

    from collections import OrderedDict
    
    dic = OrderedDict()
    
    def insert(name):
        current_dic = dic
        current_name = ''
        for name_elem in name.split('::'):
            current_name += ('::' if current_name else '') + name_elem
            if not current_name in current_dic:
                current_dic[current_name] = OrderedDict()
            current_dic = current_dic[current_name]
    
    for group in ["foo", "bar", "foo::fone", "foo::ftwo", "foo::ftwo::ffone"]:
        insert(group)
    

    这为您提供了以下结构:

    {'bar': {}, 'foo': {'foo::fone': {}, 'foo::ftwo': {'foo::ftwo::ffone': {}}}}
    

    OrderedDict 确保订单被保留,因此您不需要使用任何list。此外,您不需要使用递归,因为在 Python 中不推荐使用。

    如果你在标准库中没有OrderedDict,因为你使用的是 Python 2.6,你可以安装它:

    pip install ordereddict
    

    然后更改导入:

    from ordereddict import OrderedDict
    

    这是另一种解决方案,仅当您可以假设父母在您需要时已经存在时才有效。如果您有重复的组,事情就会变得很糟糕,因此您需要自己进行调整。

    children_of_name = dict([('', list())]) # Access root with empty string
    
    def insert(name):
        parent_name = '::'.join(name.split('::')[:-1])
        dic = dict([('name', name), ('children', list())])
        children_of_name[parent_name].append(dic)
        children_of_name[name] = dic['children']
    
    for group in ["foo", "bar", "foo::fone", "foo::ftwo", "foo::ftwo::ffone"]:
        insert(group)
    

    它为您提供了您建议的结构:

    [{'children': [{'children': [], 'name': 'foo::fone'},
                   {'children': [{'children': [], 'name': 'foo::ftwo::ffone'}],
                    'name': 'foo::ftwo'}],
      'name': 'foo'},
     {'children': [], 'name': 'bar'}]
    

    【讨论】:

    • 谢谢 - 抱歉,这是一个很棒的答案,但我不能使用 OrderedDicts,因为我们使用的是 Python 2.6
    • @IanClark 请在 OP 中列出此类要求...但是,您仍然可以查找 OrderedDict 配方;您不需要从标准库中获取它。
    • @IanClark 添加了获取 OrderedDict 的说明。
    • 谢谢 - 不幸的是我无法访问服务器,所以我需要坚持使用标准 2.6 库
    • @IanClark 您的最后一个选择是手动粘贴此配方:code.activestate.com/recipes/576693-ordered-dictionary-for-py24。我认为重新发明它没有意义。还有平方时间复杂度,你在说什么?上面的代码相对于元素的数量是线性的。您可能只会遇到非常深的嵌套问题,因为它总是从顶部向下走(不过,使用恒定的时间查找)。你可以让它更聪明地避免这种情况。考虑某种已经存在父级的二分搜索。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-21
    • 1970-01-01
    • 2012-10-18
    • 2016-06-19
    • 2017-08-04
    • 2021-03-10
    相关资源
    最近更新 更多