Sort a sublist of elements in a list leaving the rest in place

前端 未结 15 2301
小蘑菇
小蘑菇 2021-02-13 14:28

Say I have a sorted list of strings as in:

[\'A\', \'B\' , \'B1\', \'B11\', \'B2\', \'B21\', \'B22\', \'C\', \'C1\', \'C11\', \'C2\']

Now I wan

相关标签:
15条回答
  • 2021-02-13 14:55

    Most of the answers focused on the B's while I needed a more general solution as noted. Here's one:

    def _order_by_number(items):
        regex = re.compile(u'(.*?)(\d*)$') # pass a regex in for generality
        keys = {k: regex.match(k) for k in items}
        keys = {k: (v.groups()[0], int(v.groups()[1] or 0)) 
                for k, v in keys.iteritems()}
        items.sort(key=keys.__getitem__)
    

    I am still looking for a magic key however that would leave stuff in place

    0 讨论(0)
  • 2021-02-13 15:01

    You can use the natsort module:

    >>> from natsort import natsorted
    >>> 
    >>> a = ['A', 'B' , 'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2']
    >>> natsorted(a)
    ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C2', 'C11']
    
    0 讨论(0)
  • 2021-02-13 15:03

    If I'm understanding your question clear, you are trying to sort an array by two attributes; the alphabet and the trailing 'number'.

    You could just do something like

    data = ['A', 'B' , 'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2']
    data.sort(key=lambda elem: (elem[0], int(elem[1:]))
    

    but since this would throw an exception for elements without a number trailing them, we can go ahead and just make a function (we shouldn't be using lambda anyways!)

    def sortKey(elem):
         try:
                 attribute = (elem[0], int(elem[1:]))
         except:
                 attribute = (elem[0], 0)
    
         return attribute
    

    With this sorting key function made, we can sort the element in place by

    data.sort(key=sortKey)
    

    Also, you could just go ahead and adjust the sortKey function to give priority to certain alphabets if you wanted to.

    0 讨论(0)
  • 2021-02-13 15:03
    import numpy as np
    
    def sort_with_prefix(list, prefix):
        alist = np.array(list)
        ix = np.where([l.startswith(prefix) for l in list])
    
        alist[ix] =  [prefix + str(n or '')
                 for n in np.sort([int(l.split(prefix)[-1] or 0) 
                                  for l in alist[ix]])]
        return alist.tolist()
    

    For example:

    l = ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C2', 'C11']
    
    print(sort_with_prefix(l, 'B'))
    >> ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C11', 'C2']
    
    0 讨论(0)
  • 2021-02-13 15:03

    If you want to sort an arbitrary subset of elements while leaving other elements in place, it can be useful to design a view over the original list. The idea of a view in general is that it's like a lens over the original list, but modifying it will manipulate the underlying original list. Consider this helper class:

    class SubList:
        def __init__(self, items, predicate):
            self.items = items
            self.indexes = [i for i in range(len(items)) if predicate(items[i])]
    
        @property
        def values(self):
            return [self.items[i] for i in self.indexes]
    
        def sort(self, key):
            for i, v in zip(self.indexes, sorted(self.values, key=key)):
                self.items[i] = v
    

    The constructor saves the original list in self.items, and the original indexes in self.indexes, as determined by predicate. In your examples, the predicate function can be this:

    def predicate(item):
        return item.startswith('B')
    

    Then, the values property is the lens over the original list, returning a list of values picked from the original list by the original indexes.

    Finally, the sort function uses self.values to sort, and then modifies the original list.

    Consider this demo with doctests:

    def demo(values):
        """
        >>> demo(['X', 'b3', 'a', 'b1', 'b2'])
        ['X', 'b1', 'a', 'b2', 'b3']
    
        """
        def predicate(item):
            return item.startswith('b')
        sub = SubList(values, predicate)
    
        def key(item):
            return int(item[1:])
        sub.sort(key)
    
        return values
    

    Notice how SubList is used only as a tool through which to manipulate the input values. After the sub.sort call, values is modified, with elements to sort selected by the predicate function, and sorted according to the key function, and all other elements never moved.

    Using this SubList helper with appropriate predicate and key functions, you can sort arbitrary selection of elements of a list.

    0 讨论(0)
  • 2021-02-13 15:05

    If you wish to sort with different rules for different subgroups you may use tuples as sorting keys. In this case items would be grouped and sorted layer by layer: first by first tuple item, next in each subgroup by second tuple item and so on. This allows us to have different sorting rules in different subgroups. The only limit - items should be comparable within each group. For example, you cannot have int and str type keys in the same subgroup, but you can have them in different subgroups.

    Lets try to apply it to the task. We will prepare tuples with elements types (str, int) for B elements, and tuples with (str, str) for all others.

    def sorter(elem):
        letter, num = elem[0], elem[1:]
        if letter == 'B':
            return letter, int(num or 0)  # hack - if we've got `''` as num, replace it with `0`
        else:
            return letter, num
    
    data = ['A', 'B' , 'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2']
    
    sorted(data, key=sorter)
    # returns
    ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C11', 'C2']
    

    UPDATE

    If you prefer it in one line:

    data = ['A', 'B' , 'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2']
    
    sorted(data, key=lambda elem: (elem[0],  int(elem[1:] or 0) if elem[0]=='B' else elem[:1]
    # result
    ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C2', 'C11']
    

    Anyway these key functions are quite simple, so you can adopt them to real needs.

    0 讨论(0)
提交回复
热议问题