【问题标题】:How to sort hierarchical data represented in a flat list?如何对平面列表中表示的分层数据进行排序?
【发布时间】:2020-05-08 16:33:15
【问题描述】:

我有一个包含隐含层次结构的对象的 Python(2.7 版)列表。如果一个或多个 SubThing 紧跟在一个 Thing 之后,则它们属于该 Thing。我想按事物的价值对事物进行排序,当事物被移动以进行排序时,我希望它们的子事物与它们一起移动。所有这些对象都有值。示例输入:

Thing valueA
SubThing foo
SubThing bar
Thing valueB
Thing valueC
SubThing baz
SubThing flerp
...

我在 Python 2.7 中有这方面的工作代码,但它是蛮力的并且感觉不雅 - 大约 40 行代码。首先,我创建一个中间数据结构,将事物与它们的子事物分组,然后按事物的值排序,然后展平生成的结构。

我觉得有一个优雅的一两个(或三个?)衬里。这甚至听起来像是 Schwartzian Transform 的经典机会,但我并没有做出“Pythonic”的飞跃,很容易将 SubThings 与 Things 组合在一起——也许是 itertools.groupby() 的东西?

为了清楚起见:没有父事物,子事物永远不会发生。事物可能没有 SubThings。

我通过省略一系列事物/子事物可以在不相关对象之前和之后的现实来简化。看到一个解决方案通过未分类的解决方案,即在他们所处的位置,这真是太棒了,但这对我来说并没有智力上的挑战。

【问题讨论】:

    标签: python sorting data-structures


    【解决方案1】:

    您可以使用累积将父“事物”的原始索引传播到其组中的所有元素。然后将父母的值与每个组联系起来,然后对这些元组使用正常排序以保持子事物与其原始父母相关联,同时对彼此之间的父母和其父母下的孩子进行排序。

    请注意,您还需要跟踪哪个项目是父项,以便父项始终出现在其组中的第一位:

    things = ["Thing valueB",
              "SubThing foo",
              "SubThing bar",
              "Thing valueC",
              "Thing valueA",
              "SubThing baz",
              "SubThing flerp"]
    
    
    from itertools import accumulate
    
    parents = accumulate((t.startswith("Thing")*i for i,t in enumerate(things)),max)
    keys    = ((things[p],p,p<i,things[i]) for i,p in enumerate(parents))
    sortedThings = [k[-1] for k in sorted(keys)]
    
    for thing in sortedThings: print(thing)
    
    Thing valueA
    SubThing baz
    SubThing flerp
    Thing valueB
    SubThing bar
    SubThing foo
    Thing valueC
    

    这是所有的迭代器和生成器。没有中间数据结构(排序期间内部除外)。整件事可以写在一个(可怕的)行上,但我尽量让它易于理解。

    正如您所怀疑的,这确实是一个 Schwartzian 变换,因此您可以使用 keys(装饰步骤)中使用的元组来获得不同的排序方案。

    例如,如果您只想在“事物”组而不是每个组中的“子事物”项之间排序,请将keys 生成器中的(things[p],p,p&lt;i,things[i]) 替换为(things[p],p,i,things[i])

    如果您只想对每个组内的“SubThing”项进行排序而不移动组,请将其更改为(p,p&lt;i,things[i])

    [编辑] 我刚刚注意到您使用的是 Python 2.7,我认为它在 itertools 中没有累积功能。如果是这种情况,您可以自己编写:

    def accumulate(iterable,func):
        for i,value in enumerate(iterable):
            result = func(result,value) if i else value
            yield result
    

    我从未使用过 Python 2.7,因此可能存在一些我不知道的其他差异

    【讨论】:

    • 谢谢。我非常感谢您为改进和编辑所做的所有努力。我有这个在我的环境中工作,但有一个例外。我需要添加一个关键功能来对事物进行排序。我需要根据它们的值按 lower() 对它们进行排序。关于如何整合的想法?我不打算同时对 SubThings 进行排序,但由于您在元组键中内置了这种能力,我需要它们也按 lower() 排序,但它们具有 不同的 属性。可能的?也许太复杂了?
    • 只要能从“Thing”的索引中获取排序键就可以了,应该不会太复杂。例如,通过使用(things[p],p,p&lt;i,things[i].lower()) 作为元组可以实现按lower() 排序。如果您需要一些与 things[i] 相关的其他属性,您可以定义一个函数(例如 getThingKey)来获取它,并在元组中使用它:(things[p],p,p&lt;i,getThingKey(i),things[i])。这只是多了一层间接性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-18
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 2013-07-31
    • 1970-01-01
    相关资源
    最近更新 更多