【问题标题】:How to sort a list of strings following a certain pattern如何按照特定模式对字符串列表进行排序
【发布时间】:2012-04-13 15:13:29
【问题描述】:

我想对每个字符串列表进行排序,例如:

list1 = ['3DT1_PN_DIS3D_S001', '3DT1_PN_noDIS3D_S001', '3DT1_S001', '3DT1_noPN_DIS3D_S001']
list2 = ['3DT1_noPN_DIS3D_S002', '3DT1_PN_noDIS3D_S002', '3DT1_PN_DIS3D_S002']

遵循模式[ '3DT1_S##', '3DT1_noPN_DIS3D_S##', '3DT1_PN_noDIS3D_S##', '3DT1_PN_DIS3D_S##']

结果应该是:

list1 = [ '3DT1_S001', '3DT1_noPN_DIS3D_S001', '3DT1_PN_noDIS3D_S001', '3DT1_PN_DIS3D_S001']
list2 = [ '3DT1_noPN_DIS3D_S002', '3DT1_PN_noDIS3D_S002', '3DT1_PN_DIS3D_S002']

我尝试使用 sorted 方法,但没有运气!

有什么帮助吗?

【问题讨论】:

  • ## 之后还有什么意思吗?

标签: python list sorting design-patterns


【解决方案1】:

你可以定义一个key函数,按照需要的顺序返回元组,然后像这样将函数传递给sortedkey参数。

>>> def key_fn(x):
...     tags = x.split('_')
...     if tags[1][0] == 'S':
...         return (0, int(tags[1][1:]))
...     elif tags[1] == 'noPN':
...         return (1, int(tags[3][1:]))
...     elif tags[1] == 'PN':
...         if tags[2] == 'noDIS3D':
...             return (2, int(tags[3][1:]))
...         else:
...             return (3, int(tags[3][1:]))
... 
>>> list1 = ['3DT1_PN_DIS3D_S001', '3DT1_PN_noDIS3D_S001', '3DT1_S001', '3DT1_noPN_DIS3D_S001']
>>> sorted(list1, key=key_fn)
['3DT1_S001', '3DT1_noPN_DIS3D_S001', '3DT1_PN_noDIS3D_S001', '3DT1_PN_DIS3D_S001']

【讨论】:

  • @jamylak OP 的要求非常具体。答案确实符合他的要求。如果他的需求因他的特定问题而发生变化,他总是可以采用总体思路(即按排序顺序返回元组)并根据自己的要求进行修改。
  • 我想我必须同意@jamylak 的观点。如果不考虑前缀,我无法判断这是否有效。
【解决方案2】:

我的两分钱...这有一个定义订单的“patternList”变量。这可能是实现这一点的最简单(最易读、可扩展)的方法:没有凌乱的 if-else。此外,具有相同起始模式的列表项按字符串的其余部分排序。

list1.sort(key = myKey) 表示对于每个列表项,myKey 函数在排序之前执行。 myKey 函数修改已排序的列表项仅用于排序目的,以正常排序将执行您想要的方式。在输出排序列表中,未使用原始列表项(不是修改的myKey)。

在下面的示例中,myKey 函数将列表项分成两部分,并根据 patternList 变量用整数标记第一部分。普通排序可以按照你想要的方式处理返回的元组。

list1 = ['3DT1_PN_DIS3D_S001', '3DT1_PN_noDIS3D_S001', '3DT1_S001', '3DT1_noPN_DIS3D_S001']
list2 = ['3DT1_noPN_DIS3D_S002', '3DT1_PN_noDIS3D_S002', '3DT1_PN_DIS3D_S002', '3DT1_PN_DIS3D_S003', '3DT1_PN_DIS3D_S001']

def myKey(x):
    # create the 'order list' for starting pattern
    patternsList = [ '3DT1_S', '3DT1_noPN_DIS3D_S', '3DT1_PN_noDIS3D_S', '3DT1_PN_DIS3D_S']
    for i in range(len(patternsList)): # iterate patterns in order
        pattern = patternsList[i]
        if x.find(pattern) == 0: # check if x starts with pattern
            # return order value i and x without the pattern
            return (i, x.replace(pattern, '')) 

    # if undefined pattern is found, put it to first
    return (-1, x)

    # alternatively if you want undefind to be last
    # return (len(patternList)+1, x)


print list1
list1.sort(key = myKey)
print list1

print list2
list2.sort(key = myKey)
print list2

【讨论】:

    【解决方案3】:

    此方法通过按找到的第一个模式的索引进行排序来工作。

    >>> import re
    >>> list1 = ['3DT1_PN_DIS3D_S001', '3DT1_PN_noDIS3D_S001', '3DT1_S001', '3DT1_noPN_DIS3D_S001']
    >>> list2 = ['3DT1_noPN_DIS3D_S002', '3DT1_PN_noDIS3D_S002', '3DT1_PN_DIS3D_S002']
    >>> patterns = [ '3DT1_S', '3DT1_noPN_DIS3D_S', '3DT1_PN_noDIS3D_S', '3DT1_PN_DIS3D_S']
    >>> pattern = '|'.join('(%s)'%x for x in patterns)
    >>> pattern #Creates a regex pattern with each pattern as a group in order
    '(3DT1_S)|(3DT1_noPN_DIS3D_S)|(3DT1_PN_noDIS3D_S)|(3DT1_PN_DIS3D_S)'
    >>> def sort_key(x):
            return re.match(pattern,x).lastindex
    >>> list1, list2 = [sorted(l, key=sort_key) for l in (list1,list2)]
    >>> list1
    ['3DT1_S001', '3DT1_noPN_DIS3D_S001', '3DT1_PN_noDIS3D_S001', '3DT1_PN_DIS3D_S001']
    >>> list2
    ['3DT1_noPN_DIS3D_S002', '3DT1_PN_noDIS3D_S002', '3DT1_PN_DIS3D_S002']
    

    【讨论】:

      【解决方案4】:

      这是一种采用“前缀”列表的方法,这些“前缀”用于在排序之前对列表进行分组。每个项目都被添加到与 first 对应的组中,并且只有它匹配的第一个前缀。

      list1 = ['3DT1_PN_DIS3D_S001', '3DT1_PN_noDIS3D_S001', '3DT1_S001', '3DT1_noPN_DIS3D_S001']
      list2 = ['3DT1_noPN_DIS3D_S002', '3DT1_PN_noDIS3D_S002', '3DT1_PN_DIS3D_S002', '3DT1_S002']
      
      prefixes = [ '3DT1_S', '3DT1_noPN_DIS3D_S', '3DT1_PN_noDIS3D_S', '3DT1_PN_DIS3D_S']
      
      def f(l):
          result = []
          for p in prefixes:               # for each prefix, in order
              a = []                       # items in the group
              b = []                       # items not in the group
              for x in l:                  # for each item
                  if x.startswith(p):      # does the item match the prefix?
                      a.append(x)          # add it to the group
                  else:  
                      b.append(x)          # add it to the "rest"
              result.append(sorted(a))     # sort the group and save it for the result
              l = b                        # continue with the non-group elements
          return result
      

      结果如下:

      >>> f(list1)
      [['3DT1_S001'], ['3DT1_noPN_DIS3D_S001'], ['3DT1_PN_noDIS3D_S001'], ['3DT1_PN_DIS3D_S001']]
      >>> f(list2)
      [['3DT1_S002'], ['3DT1_noPN_DIS3D_S002'], ['3DT1_PN_noDIS3D_S002'], ['3DT1_PN_DIS3D_S002']]
      

      【讨论】:

      • 对于复制粘贴的目的,最好使用 # 用于 cmets,而不是 //
      • 如果一个前缀以另一个开头(例如“3DT1_noP”和“3DT1_noP2_fred”,以弥补)那么该项目不会被多次附加吗?
      • @DSM,不,它会从l中删除,所以只能放在一个组中。对于任何项目,它只会被放置在它匹配的第一个前缀组中。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-27
      • 2019-09-20
      • 2021-06-10
      • 1970-01-01
      • 2014-04-15
      • 2020-06-13
      • 2016-11-18
      相关资源
      最近更新 更多