Python &引用;“困难的”;某些条件下的排序

Python &引用;“困难的”;某些条件下的排序,python,list,sorting,object,Python,List,Sorting,Object,好的。这可能很难,但我已经挣扎了相当长一段时间,没有很大的进步,所以我想知道你们的想法 假设我有以下对象列表: objects = [ {'id': '1', 'w': 0.20}, {'id': '1.1', 'w': 0.80}, {'id': '1.2', 'w': 0.20}, {'id': '1.3', 'w': 0.30}, {'id': '1.1.1', 'w': 0.60}, {'

好的。这可能很难,但我已经挣扎了相当长一段时间,没有很大的进步,所以我想知道你们的想法

假设我有以下对象列表:

objects = [
        {'id': '1', 'w': 0.20}, 
        {'id': '1.1', 'w': 0.80}, 
        {'id': '1.2', 'w': 0.20},
        {'id': '1.3', 'w': 0.30},
        {'id': '1.1.1', 'w': 0.60},
        {'id': '1.1.2', 'w': 0.70},
        {'id': '1.1.3', 'w': 0.40},
        {'id': '1.2.1', 'w': 0.30},
    ]
我想按“id”对列表进行排序(例如,
“1”、“1.1”、“1.1.1”、“1.1.2”、“1.1.3”、“1.2”、“1.2.1”、“1.3”
),但所有具有相同父元素的元素都需要按“w”进行排序(相反)。我说的“同一个父母”是什么意思?。“1”是“1.1”、“1.2”和“1.3”的父代。同样,“1.1”是“1.1.1”、“1.1.2”、“1.1.3”的父项,“1.2”是“1.2.1”的父项。为了更好地说明这一点,假设这是一个带有嵌套注释的线程的表示(“1”是原始帖子,“1.1”是其答案,依此类推)

目前,我已经能够获得以下表格:

[ [ {'w': 0.2, 'id': '1'} ], [ {'w': 0.8, 'id': '1.1'}, {'w': 0.3, 'id': '1.3'}, 
{'w': 0.2, 'id': '1.2'} ], [ {'w': 0.7, 'id': '1.1.2'}, {'w': 0.6, 'id': '1.1.1'},
{'w': 0.4, 'id': '1.1.3'} ], [ {'w': 0.3, 'id': '1.2.1'} ] ]
如您所见,每个嵌套列表都包含作为其他元素的子元素的元素。例如,第二个嵌套列表
[{'w':0.8,'id':'1.1'},{'w':0.3,'id':'1.3'},{'w':0.2,'id':'1.2'}]
包含元素
[{'w':0.2,'id':'1'}]的所有子元素。此外,每个嵌套列表按“w”排序

最终结果应该是这样的(假设链接所有内部列表-
list(itertools.chain(*b))

基本上,首先是父元素,然后是它的子元素(按“w”排序),每个元素都是如此(如果它有子元素,当然-这里
{'id':'1.3','w':0.30}
没有子元素,所以我们不需要对它做任何事情)

我试过几件事(太复杂了,不值得解释)。我最终得到了很多条件和一个丑陋的代码

如何完成此排序


提前感谢。

使用比较器进行排序。也就是说,您编写的方法如下所示

def comparator(x, y):
    ## some code that sets value to -1 if x is "less than" y
    ## or 1 if x is "greater than or equal to" y.
    return value
然后调用
objects.sort(comparator)
,您应该被设置


这样,您一次只需比较两个项目。只要保持一致,就不会有问题。

使用比较器排序。也就是说,您编写的方法如下所示

def comparator(x, y):
    ## some code that sets value to -1 if x is "less than" y
    ## or 1 if x is "greater than or equal to" y.
    return value
然后调用
objects.sort(comparator)
,您应该被设置


这样,您一次只需比较两个项目。只要你是一致的,就不会有问题。

简单的排序不会解决你的问题,因为不可能比较任何两个元素并立即知道一个元素在另一个元素之前出现(父元素的权重可能会改变排序)

您需要将列表处理为树状结构,然后按顺序提取:

tree = {}

for d in objects:
    ids = d['id'].split('.')
    w = d['w']
    # walk into the tree, creating nodes as necessary
    subtree = [0,tree]
    for n in ids:
        if n not in subtree[1]:
            subtree[1][n] = [0,{}] # w, list of nodes
        subtree = subtree[1][n] # recurse
    # subtree is now the relevant node, set w
    subtree[0] = w

## now we have a tree:
## >>> pprint.pprint(tree, width=10)
## {'1': [0.2,
##       {'1': [0.8,
##              {'1': [0.6,
##                     {}],
##               '2': [0.7,
##                     {}],
##               '3': [0.4,
##                     {}]}],
##        '2': [0.2,
##              {'1': [0.3,
##                     {}]}],
##        '3': [0.3,
##              {}]}]}

# now walk the tree and extract the nodes:
result = []
def walk_subtree(subtree, path=[]):
    keyweights = [(subtree[key][0], key) for key in subtree]
    # walk through nodes at this level, outputting.
    for weight, key in sorted(keyweights, reverse=True):
        result.append(('.'.join(path + [key]), weight))
        walk_subtree(subtree[key][1], path=path+[key])

walk_subtree(tree)

##>>> pprint.pprint(result)
##[('1', 0.2),
## ('1.1', 0.8),
## ('1.1.2', 0.7),
## ('1.1.1', 0.6),
## ('1.1.3', 0.4),
## ('1.3', 0.3),
## ('1.2', 0.2),
## ('1.2.1', 0.3)]

简单的排序无法解决您的问题,因为无法比较任何两个元素并立即知道其中一个元素何时出现在另一个元素之前(父元素的权重可能会改变排序)

您需要将列表处理为树状结构,然后按顺序提取:

tree = {}

for d in objects:
    ids = d['id'].split('.')
    w = d['w']
    # walk into the tree, creating nodes as necessary
    subtree = [0,tree]
    for n in ids:
        if n not in subtree[1]:
            subtree[1][n] = [0,{}] # w, list of nodes
        subtree = subtree[1][n] # recurse
    # subtree is now the relevant node, set w
    subtree[0] = w

## now we have a tree:
## >>> pprint.pprint(tree, width=10)
## {'1': [0.2,
##       {'1': [0.8,
##              {'1': [0.6,
##                     {}],
##               '2': [0.7,
##                     {}],
##               '3': [0.4,
##                     {}]}],
##        '2': [0.2,
##              {'1': [0.3,
##                     {}]}],
##        '3': [0.3,
##              {}]}]}

# now walk the tree and extract the nodes:
result = []
def walk_subtree(subtree, path=[]):
    keyweights = [(subtree[key][0], key) for key in subtree]
    # walk through nodes at this level, outputting.
    for weight, key in sorted(keyweights, reverse=True):
        result.append(('.'.join(path + [key]), weight))
        walk_subtree(subtree[key][1], path=path+[key])

walk_subtree(tree)

##>>> pprint.pprint(result)
##[('1', 0.2),
## ('1.1', 0.8),
## ('1.1.2', 0.7),
## ('1.1.1', 0.6),
## ('1.1.3', 0.4),
## ('1.3', 0.3),
## ('1.2', 0.2),
## ('1.2.1', 0.3)]


作为所需结果发布的示例未按照您的描述进行排序<例如,code>1.1.2
1.1.1
之前;而
w
是任意排序的。@BurhanKhalid w是反向排序的,如果你仔细看的话,结果是有意义的。@BurhanKhalid是的,正如jamylak所说,1.1.2在前面,因为它是1.1的子级,所以根据我的描述,它们应该按“w”排序(先是0.70,然后是0.60)。抱歉,我错过了澄清。您发布的作为期望结果的示例未按照您的描述进行排序<例如,code>1.1.2在
1.1.1
之前;而
w
是任意排序的。@BurhanKhalid w是反向排序的,如果你仔细看的话,结果是有意义的。@BurhanKhalid是的,正如jamylak所说,1.1.2在前面,因为它是1.1的子级,所以根据我的描述,它们应该按“w”排序(先是0.70,然后是0.60)。很抱歉,我错过了那个澄清。然后当你移动到3.x时,它会爆炸,因为3.x不支持比较器。@IgnacioVazquez Abrams不是每个人都计划移动到3,尤其是因为它不必要地删除了这样的功能。@IgnacioVazquez Abrams你说的3.x不支持比较器是什么意思?运行所有元素所需的时间太长了?@RobertSmith:不,您只需为对象编写一个“小于”运算符:哦,现在我明白了。我认为3.x意味着3.1、3.1.1、3.2、3.2.1等等:-)。嗯,这肯定行得通,但恐怕会有点乱。此外,我真正关心的是性能,因为我很容易达到10.x.x(尽管x<10)。扩展可能非常糟糕。当你移动到3.x时,它会爆炸,因为3.x不支持比较器。@IgnacioVazquez Abrams不是每个人都计划移动到3,尤其是因为它不必要地删除了这样的功能。@IgnacioVazquez Abrams你说的3.x不支持比较器是什么意思?运行所有元素所需的时间太长了?@RobertSmith:不,您只需为对象编写一个“小于”运算符:哦,现在我明白了。我认为3.x意味着3.1、3.1.1、3.2、3.2.1等等:-)。嗯,这肯定行得通,但恐怕会有点乱。此外,我真正关心的是性能,因为我很容易达到10.x.x(尽管x<10)。这看起来很不错,虽然正如我在另一个答案中所说,我是在避开树来排序答案。至少,这是在不访问数据库的情况下完成的。我将在几个小时后看一看你的代码(这里有点晚了),但非常感谢:-)。@RobertSmith-我认为你不能在这里避免树或类似树的结构,因为你试图做的是在一个非常基本的意义上基于树的(父母/孩子/等等)。@jnnnnn这个解决方案给我留下了深刻的印象。让我问你,这是标准程序吗?它似乎是在使用字典通过引用传递的事实来构造树,同时实际修改子树。这是一个