比较有关其各自元素后缀的字符串列表

Compare lists of strings regarding the suffixes of their respective elements

假设我有一个这样的列表

myList = ['A_x','B_x','C_x','D_x']

和这样的列表列表

myListOfList = [['A_x','B_y','C_x','D_z'],
                ['A_y','B_y','C_y','D_y'],
                ['A_u','B_y','C_y','D_y'],
                ['A_y','C_y','B_y','D_y', 'E_l'],
                ['A_y','P_y','T_y','D_y'],
                ['A_y','B_y','C_y'],
                ['A_y','C_y','D_y','B_y'],
                ['A_z','C_z','D_z','B_z']]

现在我想确定 myListOfList 中与 myList 相同的所有列表,除了这些列表中各个元素的后缀,其中 [的子列表中元素的后缀=16=] 必须相同。所以在上面的例子中我想找到:

#all elements as in myList but with suffix y instead of x
['A_y','B_y','C_y','D_y']
#all elements as in myList but with suffix y instead of x but in different order
['A_y','C_y','D_y','B_y']
#all elements as in myList but with suffix z instead of x but in different order
['A_z','C_z','D_z','B_z']

但我不想找到其他的,因为它们的元素没有相同的后缀,长度与 myList's 长度不同,或者元素的第一部分与 [=17] 中的元素不同=].

我是这样实现的:

myList = ['A_x','B_x','C_x','D_x']

myListOfList = [['A_x','B_y','C_x','D_z'],
                ['A_y','B_y','C_y','D_y'],
                ['A_u','B_y','C_y','D_y'],
                ['A_y','C_y','B_y','D_y', 'E_l'],
                ['A_y','P_y','T_y','D_y'],
                ['A_y','B_y','C_y'],
                ['A_y','C_y','D_y','B_y'],
                ['A_z','C_z','D_z','B_z']]

listOfInd=[]                
for ind, sl in enumerate(myListOfList):
    if len(sl) == len(myList) and [x.endswith(sl[0][-2:]) for x in sl].count(False) == 0:
        tempList = zip(myList, sorted(sl))
        allTrue = True
        for el in tempList:
            #print el[0]
            if el[0][:-2] != el[1][:-2]:
                allTrue = False

        if(allTrue):
            listOfInd.append(ind)

这确实给了我正确的输出:listOfInd = [1, 6, 7].

解释这段代码的两个元素:

这将检查列表 sl 中的所有元素是否具有相同的后缀:

[x.endswith(sl[0][-2:]) for x in sl].count(False) == 0

这将检查字符串 el 除了后缀(字符串的最后两个元素)之外是否相同:

if el[0][:-2] != el[1][:-2]

所以我要做的是遍历[=​​16=]中的每个列表,如果这个列表中的元素数量与myList中的相同并且这个列表中的所有元素都具有相同的后缀,我zip这个榜单跟myList。然后我遍历这个压缩列表中的每个元素并比较字符串直到后缀。

该代码有效,但是,它看起来非常低效,我想知道是否有更多 "pythonic" 方法可以在没有这么多循环的情况下执行此操作。谁能想到一种更有效的实现方式?

这是我使用集合的尝试。请注意,我使用的是字符串子索引而不是拆分“_”或正则表达式,因为我假设的格式非常严格。

myList = ['A_x','B_x','C_x','D_x']

myListOfList = [['A_x','B_y','C_x','D_z'],
                ['A_y','B_y','C_y','D_y'],
                ['A_u','B_y','C_y','D_y'],
                ['A_y','C_y','B_y','D_y', 'E_l'],
                ['A_y','P_y','T_y','D_y'],
                ['A_y','B_y','C_y'],
                ['A_y','C_y','D_y','B_y'],
                ['A_z','C_z','D_z','B_z']]

myList_prefixes = set((x[0] for x in myList))

listOfInd = []
for idx, sublist in enumerate(myListOfList):
    if len(sublist) != len(myList):
        continue
    sublist_suffixes = set((x[-1] for x in sublist))
    # ensure that you only have one kind of suffix, like only x or only y
    if len(sublist_suffixes) != 1:
        continue
    sublist_prefixes = set((x[0] for x in sublist))
    # make sure the prefixes match
    if sublist_prefixes != myList_prefixes:
        continue
    listOfInd.append(idx)
print listOfInd

如果前缀集,检查前缀集是否是子列表中每个元素的前缀的子集,如果是则检查所有后缀是否相同。

st = {s[0] for s in myList}
l = []
for ind, sub in enumerate(myListOfList):
    k = sub[0][-1]
    if st.issubset(ch[0] for ch in sub) and all((ch[-1] == k for ch in sub)):
        l.append(ind)
print(l)
[1, 6, 7]

如果您愿意,可以将其放在列表中:

inds = [ind for ind, sub in enumerate(myListOfList) if st.issubset(ch[0] for ch in sub)
        and all(ch[-1] == sub[0][-1] for ch in sub)]

print(inds)
[1, 6, 7]

您还可以进行一些优化,如果任何子项的长度不等于 myList 的长度,我们将无法匹配:,这样我们就可以使用 all 来查看每个元素[0]是否来自每个sub 在集合中,因为它可能会快一点。

l = []
ln_m = len(myList)
for ind, sub in enumerate(myListOfList):
    k = sub[0][-1]
    if len(sub) == ln_m and all(ch[0] in st for ch in sub) and all(ch[-1] == k for ch in sub):
        l.append(ind)
def compare(l1,l2):
    def normalize(l):
        r = defaultdict(set)
        for a,b in map(lambda x: x.split('_'), l): r[b].add(a)
        return set(tuple(sorted(v)) for v in r.values())
    n1,n2 = normalize(l1),normalize(l2)
    return len(n1)==1 and len(n2)==1 and n1==n2

测试:

>>> [i for i,l in enumerate(myListOfList) if compare(myList,l)]
[1, 6, 7]

关于 compare() 函数内的 normalize() 函数的一点评论:它将为每个后缀生成一组排序的前缀:

>>> normalize(myList)
{('A', 'B', 'C', 'D')}

>>> normalize(['A_x','B_x','C_x','D_y'])
{('A', 'B', 'C'), ('D',)}

>>> normalize(['A_x','B_y','C_z','D_x'])
{('A', 'D'), ('B',), ('C',)}

事实上,如果您删除 len(n1)==1 and len(n2)==1 条件,您可以像我对您的问题的评论那样比较列表。