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

前端 未结 15 2271
小蘑菇
小蘑菇 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:44

    To answer precisely what you describe you can do this :

    l = ['A', 'B' , 'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2', 'D']
    
    
    def custom_sort(data, c):
        s = next(i for i, x in enumerate(data) if x.startswith(c))
        e = next((i for i, x in enumerate(data) if not x.startswith(c) and i > s), -1)
        return data[:s] + sorted(data[s:e], key=lambda d: int(d[1:] or -1)) + data[e:]
    
    
    print(custom_sort(l, "B"))
    

    if you what an complete sort you can simply do this (as @Mike JS Choi answered but simplier) :

    output = sorted(l, key=lambda elem: (elem[0], int(elem[1:] or -1)))
    
    0 讨论(0)
  • 2021-02-13 14:46

    If the elements that are to be sorted are all adjacent to each other in the list:

    You can use cmp in the sorted()-function instead of key:

    s1=['A', 'B' , 'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2']
    
    def compare(a,b):
        if (a[0],b[0])==('B','B'): #change to whichever condition you'd like
            inta=int(a[1:] or 0)
            intb=int(b[1:] or 0)
            return cmp(inta,intb)  #change to whichever mode of comparison you'd like
        else:
            return 0               #if one of a, b doesn't fulfill the condition, do nothing
    
    sorted(s1,cmp=compare)
    

    This assumes transitivity for the comparator, which is not true for a more general case. This is also much slower than using key, but the advantage is that it can take context into account (to a small extent).

    If the elements that are to be sorted are not all adjacent to each other in the list:

    You could generalise the comparison-type sorting algorithms by checking every other element in the list, and not just neighbours:

    s1=['11', '2', 'A', 'B', 'B11', 'B21', 'B1', 'B2', 'C', 'C11', 'C2', 'B09','C8','B19']
    
    def cond1(a):           #change this to whichever condition you'd like
        return a[0]=='B'
    
    def comparison(a,b):    #change this to whichever type of comparison you'd like to make
        inta=int(a[1:] or 0)
        intb=int(b[1:] or 0)
        return cmp(inta,intb)
    
    def n2CompareSort(alist,condition,comparison):
        for i in xrange(len(alist)):
            for j in xrange(i):
                if condition(alist[i]) and condition(alist[j]):
                    if comparison(alist[i],alist[j])==-1:
                        alist[i], alist[j] = alist[j], alist[i]  #in-place swap
    
    n2CompareSort(s1,cond1,comparison)
    

    I don't think that any of this is less of a hassle than making a separate list/tuple, but it is "in-place" and leaves elements that don't fulfill our condition untouched.

    0 讨论(0)
  • 2021-02-13 14:48

    You can use the following key function. It will return a tuple of the form (letter, number) if there is a number, or of the form (letter,) if there is no number. This works since ('A',) < ('A', 1).

    import re
    
    a = ['A', 'B' ,'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2']
    
    regex = re.compile(r'(\d+)')
    
    def order(e):
        num = regex.findall(e)
        if num:
            num = int(num[0])
            return e[0], num
        return e,
    
    print(sorted(a, key=order))
    >> ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C2', 'C11']
    
    0 讨论(0)
  • 2021-02-13 14:49
    import re
    from collections import OrderedDict
    
    a = ['A', 'B' , 'B1', 'B11', 'B2', 'B21', 'B22', 'C', 'C1', 'C11', 'C2']
    dict = OrderedDict()
    
    def get_str(item):
      _str = list(map(str, re.findall(r"[A-Za-z]", item)))
      return _str
    
    def get_digit(item):
      _digit = list(map(int, re.findall(r"\d+", item)))
      return _digit
    
    for item in a:
      _str = get_str(item)
      dict[_str[0]] = sorted([get_digit(dig) for dig in a if _str[0] in dig])
    
    nested_result = [[("{0}{1}".format(k,v[0]) if v else k) for v in dict[k]] for k in dict.keys()]
    print (nested_result)
    # >>> [['A'], ['B', 'B1', 'B2', 'B11', 'B21', 'B22'], ['C', 'C1', 'C2', 'C11']]
    
    result = []
    for k in dict.keys():
      for v in dict[k]:
        result.append("{0}{1}".format(k,v[0]) if v else k)
    print (result)
    # >>> ['A', 'B', 'B1', 'B2', 'B11', 'B21', 'B22', 'C', 'C1', 'C2', 'C11']
    
    0 讨论(0)
  • 2021-02-13 14:51
    def compound_sort(input_list, natural_sort_prefixes=()):
        padding = '{:0>%s}' % len(max(input_list, key=len))
        return sorted(
            input_list, 
            key = lambda li: \
                ''.join(
                    [li for c in '_' if not li.startswith(natural_sort_prefixes)] or 
                    [c for c in li if not c.isdigit()] + \
                    [c for c in padding.format(li) if c.isdigit()]
                )
            )
    

    This sort method receives:

    • input_list: The list to be sorted,
    • natural_sort_prefixes: A string or a tuple of strings.

    List items targeted by the natural_sort_prefixes will be sorted naturally. Items not matching those prefixes will be sorted lexicographically.

    This method assumes that the list items are structured as one or more non-numerical characters followed by one or more digits.

    It should be more performant than solutions using regex, and doesn't depend on external libraries.

    You can use it like:

    print compound_sort(['A', 'B' , 'B11', 'B1', 'B2', 'C11', 'C2'], natural_sort_prefixes=("A","B"))
    
    # ['A', 'B', 'B1', 'B2', 'B11', 'C11', 'C2']
    
    0 讨论(0)
  • 2021-02-13 14:53

    You can use ord() to transform for exemple 'B11' in numerical value:

    cells = ['B11', 'C1', 'A', 'B1', 'B2', 'B21', 'B22', 'C11', 'C2', 'B']
    conv_cells = []
    
    ## Transform expression in numerical value.
    for x, cell in enumerate(cells):
        val = ord(cell[0]) * (ord(cell[0]) - 65) ## Add weight to ensure respect order.
        if len(cell) > 1:
            val += int(cell[1:])
        conv_cells.append((val, x)) ## List of tuple (num_val, index).
    
    ## Display result.
    for x in sorted(conv_cells):
        print(str(cells[x[1]]) + ' - ' + str(x[0]))
    
    0 讨论(0)
提交回复
热议问题