首页 文章

按多个属性对列表进行排序?

提问于
浏览
300

我有一份清单清单:

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

如果我想按一个元素排序,比如高/短元素,我可以通过 s = sorted(s, key = itemgetter(1)) 来完成 .

如果我想按高/短和颜色排序,我可以对每个元素进行两次排序,但是有更快的方法吗?

4 回答

  • 3

    这是一种方法:你基本上重写你的排序函数来获取排序函数列表,每个排序函数比较你想要测试的属性,在每个排序测试中,你看,看看cmp函数是否返回非零返回如果是这样打破并发送返回值 . 您可以通过调用Lambda列表函数的Lambda来调用它 .

    它的优点是它可以单独传递数据,而不是像其他方法那样的前一种数据 . 另一件事是它排序到位,而排序似乎复制 .

    我用它来编写一个排名函数,它对每个对象在一个组中的类列表进行排序,并且有一个得分函数,但是你可以添加任何属性列表 . 注意非lambda式,虽然hackish使用lambda来调用setter . 排名部分不适用于列表数组,但排序将 .

    #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)
    

    这是一种对对象列表进行排名的方法

    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
    
  • 504

    您似乎可以使用 list 而不是 tuple . 当你抓取属性而不是列表/元组的'magic indexes'时,我认为这变得更加重要 .

    在我的情况下,我想按类的多个属性排序,其中传入的键是字符串 . 我需要在不同的地方进行不同的排序,我想要客户端与之交互的父类的常见默认排序;我真的需要时只需要覆盖'排序键',但也可以将它们存储为类可以共享的列表

    所以首先我定义了一个辅助方法

    def attr_sort(self, attrs=['someAttributeString']:
      '''helper to sort by the attributes named by strings of attrs in order'''
      return lambda k: [ getattr(k, attr) for attr in attrs ]
    

    然后使用它

    # would defined elsewhere but showing here for consiseness
    self.SortListA = ['attrA', 'attrB']
    self.SortListB = ['attrC', 'attrA']
    records = .... #list of my objects to sort
    records.sort(key=self.attr_sort(attrs=self.SortListA))
    # perhaps later nearby or in another function
    more_records = .... #another list
    more_records.sort(key=self.attr_sort(attrs=self.SortListB))
    

    这将使用生成的lambda函数按 object.attrA 排序列表,然后 object.attrB 假设 object 具有与提供的字符串名称对应的getter . 第二种情况将按 object.attrC 然后 object.attrA 排序 .

    这也允许你潜在地将外向排序选择暴露给消费者,单元测试,或者他们可能告诉你他们希望如何为api中的某些操作完成排序,只需要给你一个列表而不是将它们耦合到您的后端实现 .

  • 1

    键可以是返回元组的函数:

    s = sorted(s, key = lambda x: (x[1], x[2]))
    

    或者你可以使用_283307实现相同的功能(更快,避免Python函数调用):

    import operator
    s = sorted(s, key = operator.itemgetter(1, 2))
    

    请注意,在这里您可以使用 sort 而不是使用 sorted 然后重新分配:

    s.sort(key = operator.itemgetter(1, 2))
    
  • 21

    我不确定这是否是最pythonic方法...我有一个元组列表,需要按降序整数值排序第一,按字母顺序排序第二 . 这需要反转整数排序,但不是按字母顺序排序 . 这是我的解决方案:(在考试顺便说一下,我甚至不知道你可以'嵌套'排序功能)

    a = [('Al', 2),('Bill', 1),('Carol', 2), ('Abel', 3), ('Zeke', 2), ('Chris', 1)]  
    b = sorted(sorted(a, key = lambda x : x[0]), key = lambda x : x[1], reverse = True)  
    print(b)  
    [('Abel', 3), ('Al', 2), ('Carol', 2), ('Zeke', 2), ('Bill', 1), ('Chris', 1)]
    

相关问题