Sort a list by multiple attributes?

前端 未结 6 1987
后悔当初
后悔当初 2020-11-22 02:48

I have a list of lists:

[[12, \'tall\', \'blue\', 1],
[2, \'short\', \'red\', 9],
[4, \'tall\', \'blue\', 13]]

If I wanted to sort by one e

6条回答
  •  花落未央
    2020-11-22 03:11

    Here's one way: You basically re-write your sort function to take a list of sort functions, each sort function compares the attributes you want to test, on each sort test, you look and see if the cmp function returns a non-zero return if so break and send the return value. You call it by calling a Lambda of a function of a list of Lambdas.

    Its advantage is that it does single pass through the data not a sort of a previous sort as other methods do. Another thing is that it sorts in place, whereas sorted seems to make a copy.

    I used it to write a rank function, that ranks a list of classes where each object is in a group and has a score function, but you can add any list of attributes. Note the un-lambda-like, though hackish use of a lambda to call a setter. The rank part won't work for an array of lists, but the sort will.

    #First, here's  a pure list version
    my_sortLambdaLst = [lambda x,y:cmp(x[0], y[0]), lambda x,y:cmp(x[1], y[1])]
    def multi_attribute_sort(x,y):
        r = 0
        for l in my_sortLambdaLst:
            r = l(x,y)
            if r!=0: return r #keep looping till you see a difference
        return r
    
    Lst = [(4, 2.0), (4, 0.01), (4, 0.9), (4, 0.999),(4, 0.2), (1, 2.0), (1, 0.01), (1, 0.9), (1, 0.999), (1, 0.2) ]
    Lst.sort(lambda x,y:multi_attribute_sort(x,y)) #The Lambda of the Lambda
    for rec in Lst: print str(rec)
    

    Here's a way to rank a list of objects

    class probe:
        def __init__(self, group, score):
            self.group = group
            self.score = score
            self.rank =-1
        def set_rank(self, r):
            self.rank = r
        def __str__(self):
            return '\t'.join([str(self.group), str(self.score), str(self.rank)]) 
    
    
    def RankLst(inLst, group_lambda= lambda x:x.group, sortLambdaLst = [lambda x,y:cmp(x.group, y.group), lambda x,y:cmp(x.score, y.score)], SetRank_Lambda = lambda x, rank:x.set_rank(rank)):
        #Inner function is the only way (I could think of) to pass the sortLambdaLst into a sort function
        def multi_attribute_sort(x,y):
            r = 0
            for l in sortLambdaLst:
                r = l(x,y)
                if r!=0: return r #keep looping till you see a difference
            return r
    
        inLst.sort(lambda x,y:multi_attribute_sort(x,y))
        #Now Rank your probes
        rank = 0
        last_group = group_lambda(inLst[0])
        for i in range(len(inLst)):
            rec = inLst[i]
            group = group_lambda(rec)
            if last_group == group: 
                rank+=1
            else:
                rank=1
                last_group = group
            SetRank_Lambda(inLst[i], rank) #This is pure evil!! The lambda purists are gnashing their teeth
    
    Lst = [probe(4, 2.0), probe(4, 0.01), probe(4, 0.9), probe(4, 0.999), probe(4, 0.2), probe(1, 2.0), probe(1, 0.01), probe(1, 0.9), probe(1, 0.999), probe(1, 0.2) ]
    
    RankLst(Lst, group_lambda= lambda x:x.group, sortLambdaLst = [lambda x,y:cmp(x.group, y.group), lambda x,y:cmp(x.score, y.score)], SetRank_Lambda = lambda x, rank:x.set_rank(rank))
    print '\t'.join(['group', 'score', 'rank']) 
    for r in Lst: print r
    

提交回复
热议问题