如何对平面列表中表示的分层数据进行排序



我有一个Python(2.7版(的对象列表,其中包含隐含的层次结构。如果一个或多个子事物立即跟随一个事物,则它们属于该事物。我想根据事物的价值对其进行排序,当事物被移动进行排序时,我希望它们的SubThings也随之移动。所有这些对象都有值。示例输入:

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

我在Python 2.7中有这方面的工作代码,但这是一种蛮力,感觉很不雅——大约有40行代码。首先,我创建了一个中间数据结构,用它们的SubThings对Things进行分组,然后按Things的值进行排序,然后将得到的结构压平。

我有一种感觉,有一个优雅的一两(或三?(内衬。这听起来甚至像是Schwartzian变换的经典机会,但我并没有像"Python"那样轻松地将SubThings与Things分组——也许是使用itertools.groupby((?

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

我通过省略一个事实进行了简化,即Things/SubThings系列的前面和后面可以是不相关的对象。如果能看到一个解决方案,让那些未排序的人通过,即处于他们原来的位置,那将是一件很棒的事情,但这对我来说在智力上没有那么具有挑战性。

您可以使用accumulate将父"Thing"的原始索引传播到其组中的所有元素。然后将父对象的值绑定到每个组,然后对这些元组使用正常排序,以保持子对象绑定到其原始父对象,同时对彼此之间的父对象和父对象下的子对象进行排序。

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

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(装饰步骤(中使用的元组来获得不同的排序方案。

例如,如果只想在"Thing"组之间排序,而不想在每个组中的"SubThing"项之间排序,请在keys生成器中将(things[p],p,p<i,things[i])替换为(things[p],p,i,things[i])

如果您只想对每组中的"SubThing"项目进行排序,而不想移动组,请将其更改为(p,p<i,things[i])

[EDIT]我刚刚注意到您使用的是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,因此可能还有一些我不知道的其他差异

最新更新