在列表中交叉字符串的快捷方式

时间:2016-02-02 15:28:17

标签: python list concatenation

如果有这样的列表:

shops=['A','B','C','D']

并且想要创建以下新列表(我将每个元素彼此交叉并创建一个字符串,其中第一部分在第二部分之前是字母数字):

['A-B', 'A-C', 'A-D']

['A-B', 'B-C', 'B-D']

['A-C', 'B-C', 'C-D']

['A-D', 'B-D', 'C-D']

我有这样的事情:

for a in shops:
    cons = []
    for b in shops:
        if a!=b:
            con = [a,b]
            con = sorted(con, key=lambda x: float(x))
            cons.append(con[0]+'-'+con[1])
    print(cons)

然而,对于大型列表来说这是非常慢的(例如1000我有1000 * 999 * 0.5输出)。我一直在寻找一种更有效的方法吗?

我可以使用if-else子句进行排序,例如

for a in shops:
    cons = []
    for b in shops:
        if a<b:
            cons.append(a+"-"+b)
        elif a>b:
            cons.append(b+"-"+a)
    print(cons)

其中,我还没有定时 - 但是我认为主要的减速是双循环

3 个答案:

答案 0 :(得分:9)

您可以使用一些额外的检查来创建嵌套列表理解:

>>> shops=['A','B','C','D']
>>> [["-".join((min(a,b), max(a,b))) for b in shops if b != a] for a in shops]
[['A-B', 'A-C', 'A-D'],
 ['A-B', 'B-C', 'B-D'],
 ['A-C', 'B-C', 'C-D'],
 ['A-D', 'B-D', 'C-D']]

请注意,这可能不会比您的代码快得多,因为您仍然必须生成所有这些组合。在实践中,您可以将其设置为生成器表达式,因此元素不会一次全部生成,而只是“根据需要”生成:

gen = (["-".join((min(a,b), max(a,b))) for b in shops if b != a] for a in shops)
for item in gen:
    print(item)

更新:我使用IPython的%timeit进行了一些时序分析。结果证明你的第二次实施是最快的。使用100个字符串(map(str, range(100)))的列表进行测试,然后将每个方法转换为生成器。

In [32]: %timeit list(test.f1())         # your first implementation
100 loops, best of 3: 13.5 ms per loop

In [33]: %timeit list(test.f2())         # your second implementation
1000 loops, best of 3: 1.63 ms per loop

In [34]: %timeit list(test.g())          # my implementation
100 loops, best of 3: 3.49 ms per loop

您可以使用简单的if/else代替min/max加快速度,就像在第二次实施中一样,它们的速度也相同。

(["-".join((a,b) if a < b else (b,a)) for b in shops if b != a] for a in shops)

答案 1 :(得分:4)

如果列表已排序且没有重复项,您可以在列表中跟踪您的位置,以避免必须进行比较才能获得订单。

from itertools import chain, islice

combos = []
for i, s in enumerate(shops):
    combo = ['{0}-{1}'.format(a, b) for a, b in chain(
        ((c, s) for c in islice(shops, None, i),
        ((s, c) for c in islice(shops, i+1))]
    combos.append(combo)

编辑:更新为使用生成器

答案 2 :(得分:2)

根据你的说法:

  

我互相交叉每个元素并在第一个字符串中创建一个字符串   部分是在第二个之前的字母数字

您可以使用以下两种组合:

>>> form itertools import combinations
>>> list(combinations(['_'.join(i) for i in combinations(shops,2)],3)
... )
[('A_B', 'A_C', 'A_D'), ('A_B', 'A_C', 'B_C'), ('A_B', 'A_C', 'B_D'), ('A_B', 'A_C', 'C_D'), ('A_B', 'A_D', 'B_C'), ('A_B', 'A_D', 'B_D'), ('A_B', 'A_D', 'C_D'), ('A_B', 'B_C', 'B_D'), ('A_B', 'B_C', 'C_D'), ('A_B', 'B_D', 'C_D'), ('A_C', 'A_D', 'B_C'), ('A_C', 'A_D', 'B_D'), ('A_C', 'A_D', 'C_D'), ('A_C', 'B_C', 'B_D'), ('A_C', 'B_C', 'C_D'), ('A_C', 'B_D', 'C_D'), ('A_D', 'B_C', 'B_D'), ('A_D', 'B_C', 'C_D'), ('A_D', 'B_D', 'C_D'), ('B_C', 'B_D', 'C_D')]
>>> 

首先,您可以在列表推导中使用组合来创建有序对,并使用str.join将它们连接起来。然后使用其他组合来创建三元组。