代码之家  ›  专栏  ›  技术社区  ›  Andrej Kesely

将自定义排序函数归纳为多列(升序/降序)

  •  0
  • Andrej Kesely  · 技术社区  · 6 年前

    这个问题是基于 previous Stackoverflow answer .这个问题对我来说似乎太大了。

    我现在有排序功能 sort_multi(lst, index_normal, index_reversed) 它接受输入列表,以及两个参数,指示哪一列按正常顺序排序,哪一列按相反顺序排序:

    from pprint import pprint
    from itertools import groupby, chain
    
    l = ((1, 'b'),
         (1, 'd'),
         (2, 'a'),
         (1, 'a'))
    
    def sort_multi(lst, index_normal, index_reversed):
        return list(chain.from_iterable([sorted(list(j), key=lambda v:v[index_reversed], reverse=True) for i, j in groupby(sorted(lst), key=lambda v:v[index_normal])]))
    
    pprint(sort_multi(l, 0, 1), width=10)  # index 0 is sorted normally, 1 in reversed order.
    

    输出:

    [(1, 'd'),
     (1, 'b'),
     (1, 'a'),
     (2, 'a')]
    

    我想要的是扩展函数 任何 索引数。如。

    例如,输入3列:

    l = ((1, 'c', 'aa'),
         (2, 'b', 'bb'),
         (1, 'c', 'cc'),
         (1, 'd', 'dd'))
    
    sort_multi(l, reversed=(False, True, True))
    

    给出输出:

    [(1, 'd', 'dd'),
     (1, 'c', 'cc'),
     (1, 'c', 'aa'),
     (2, 'b', 'bb')]
    

    元组可以包含字符串、整数…

    2 回复  |  直到 6 年前
        1
  •  2
  •   Kevin    6 年前

    递归方法:按第一个条件对列表进行排序。然后按该条件分组,并使用剩余的条件递归排序每个子组。

    from operator import itemgetter
    import itertools
    
    def multisorted(seq, indices, reversed_states):
        if len(indices) == 0:
            return seq
        index = indices[0]
        reversed = reversed_states[0]
        partially_sorted_seq = sorted(seq, key = itemgetter(indices[0]), reverse=reversed_states[0])
        result = []
        for key, group in itertools.groupby(partially_sorted_seq, key=itemgetter(indices[0])):
            result.extend(multisorted(group, indices[1:], reversed_states[1:]))
        return result
    
    d = ((1, 'c', 'aa'),
         (2, 'b', 'bb'),
         (1, 'c', 'cc'),
         (1, 'd', 'dd'))
    
    print(multisorted(d, (0,1,2), (False, True, True)))
    

    结果:

    [(1, 'd', 'dd'), (1, 'c', 'cc'), (1, 'c', 'aa'), (2, 'b', 'bb')]
    

    方法2。 sort sorted 已经可以按多个条件进行排序。我们只需要更改键,使每个元组的某些元素在不相等比较方面具有颠倒的逻辑。

    from functools import total_ordering
    
    @total_ordering
    class Negated:
        def __init__(self, value):
            self.value = value
        def __eq__(self, other):
            return self.value == other.value
        def __lt__(self, other):
            return self.value > other.value
    
    def multisorted(seq, ordering):
        return sorted(seq, key = lambda t: [Negated(x) if reversed else x for x, reversed in zip(t, ordering)])
    
    d = ((1, 'c', 'aa'),
         (2, 'b', 'bb'),
         (1, 'c', 'cc'),
         (1, 'd', 'dd'))
    
    print(multisorted(d, (False, True, True)))
    
        2
  •  2
  •   Dusan Gligoric    6 年前

    你可以做一些简单的事情,比如:

    from operator import itemgetter
    
    def sort_any_multi(lst, index_order_reversed):
        for index, reverse in reversed(index_order_reversed):
           lst = sorted(lst, key=itemgetter(index), reverse=reverse)
        return lst
    
    r = ((1, 'c', 'aa'),
         (2, 'b', 'bb'),
         (1, 'c', 'cc'),
         (1, 'd', 'dd'))
    
    print(sort_any_multi(r, [[0, False], [1, True], [2, True]]))
    

    释放:

    [(1, 'd', 'dd'), (1, 'c', 'cc'), (1, 'c', 'aa'), (2, 'b', 'bb')]
    

    技巧是从最后一个优先级索引到最高优先级索引进行排序。