如何按照特定模式对字符串列表进行排序

5

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

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方法玩一下,但没有成功!

有人能帮忙吗?


在这之后,## 有任何特殊含义吗? - jamylak
4个回答

4
你可以定义一个返回所需顺序元组的“键函数”,然后将该函数传递给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的要求非常具体。答案确实符合他的要求。如果他的特定问题的要求发生变化,他总是可以采用通用思路(即按排序顺序返回元组)并根据自己的要求进行修改。 - Praveen Gollakota
1
我想我必须同意@jamylak的观点。如果不考虑前缀,我无法确定这是否有效。 - DSM

4

个人意见...这里有一个'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

1

这种方法是通过按找到的第一个模式的索引进行排序来实现的。

>>> 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']

-1
这里有一种方法,它接受一个“前缀”列表,用于在排序之前对列表进行分组。每个项目都添加到与其匹配的第一个前缀对应的组中,仅限于第一个前缀。
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']]

为了复制和粘贴的目的,最好使用作为注释,而不是// - DSM
如果一个前缀以另一个前缀开头(例如“3DT1_noP”和“3DT1_noP2_fred”,这只是举个例子),那么该项不会被多次添加吗? - DSM
@DSM,不会,它将从l中删除,因此它只能放置在一个组中。对于任何项目,它只会放置在第一个前缀组中匹配的位置。 - dhg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接