从多个列表中创建一个列表

6

我有三个列表:

list_01 = ['DOG','CAT','BEAR']
list_02 = ['V','W','X','Y','Z']
list_03 = ['A','B','C','D','E','F','G','H']

我希望得到的是以下类似的清单:

list_04 = ['DOG','V','A','CAT','W','B','BEAR','X','C','Y','D','Z','E','F','G','H']

这个列表应该包含来自列表1的一个项目,接着是列表2中的一个项目,然后是列表3中的一个项目。然后继续这样做,直到列表1用完为止;列表1应该被忽略,然后同样的过程应该在列表2和3上进行,一直进行到所有列表都为空。

8个回答

6

看起来您想按顺序执行而不是随机执行。如果是这样,您可以使用来自itertools的zip_longest(),并编写一个嵌套的列表推导式:

from itertools import zip_longest

list_01 = ['DOG','CAT','BEAR']
list_02 = ['V','W','X','Y','Z']
list_03 = ['A','B','C','D','E','F','G','H']


list_04 = [n for group in zip_longest(list_01, list_02, list_03) 
           for n in group if n is not None]

# ['DOG', 'V', 'A', 'CAT', 'W', 'B', 'BEAR', 'X', 'C', 'Y', 'D', 'Z', 'E', 'F', 'G', 'H']

注意:zip_longest 会在一个列表耗尽时产生None值。这就是为什么我们在推导式中过滤None的原因。

6

您可以使用来自itertools模块的zip_longestchain

from itertools import chain, zip_longest
list_04 = [i for i in chain(*zip_longest(list_01, list_02, list_03))
           if i is not None]

输出:

['DOG', 'V', 'A', 'CAT', 'W', 'B', 'BEAR', 'X', 'C', 'Y', 'D', 'Z', 'E', 'F', 'G', 'H']

这将删除所有假值,而不仅仅是None,这可能会对OP造成问题。 - AKX
@AKX,我看到了你有趣的替代方案。我倾向于不过早优化,OP的示例似乎表明不会出现任何虚假值 ;) - mozway
顺便问一下,当列表中包含None时如何处理? - eroot163pi
@eroot163pi 在 zip_longest 中,您可以使用 fillvalue=XXX 并填充一个您知道不会匹配的值,然后测试其相等性。 - mozway
1
@eroot163pi,请检查AKX的迭代器,这是它的作用。 - mozway
@eroot163pi "构造一个不在列表中的元素很困难" - 哪里,这很简单,通常使用 object() 就可以实现。 - no comment

3
最简单的方法:
result = list(roundrobin(list_01, list_02, list_03))

只需从Itertools Recipes复制&粘贴roundrobin,或者根据配方部分所提到的,在more-itertools中导入它。


另外,我喜欢使用heapq.merge

result = list(map(itemgetter(1), merge(*map(enumerate, lists), key=itemgetter(0))))

变种:

index, value = map(itemgetter, (0, 1))
result = list(map(value, merge(*map(enumerate, lists), key=index)))

完整代码(在线尝试):

from operator import itemgetter
from heapq import merge

list_01 = ['DOG','CAT','BEAR']
list_02 = ['V','W','X','Y','Z']
list_03 = ['A','B','C','D','E','F','G','H']
lists = list_01, list_02, list_03
expect = ['DOG','V','A','CAT','W','B','BEAR','X','C','Y','D','Z','E','F','G','H']

result = list(map(itemgetter(1), merge(*map(enumerate, lists), key=itemgetter(0))))

print(result == expect)

index, value = map(itemgetter, (0, 1))
result = list(map(value, merge(*map(enumerate, lists), key=index)))

print(result == expect)

2
list_01 = ['DOG','CAT','BEAR']
list_02 = ['V','W','X','Y','Z']
list_03 = ['A','B','C','D','E','F','G','H']
L = [list_01, list_02, list_03]


def shuffle(L):
    result = []

    while any(L):
        for sub in L:
            if sub:
                result.append(sub.pop(0))

    return result

print(shuffle(L))

输出:

['DOG', 'V', 'A', 'CAT', 'W', 'B', 'BEAR', 'X', 'C', 'Y', 'D', 'Z', 'E', 'F', 'G', 'H']

你的意思是不将子列表放入另一个列表中吗? - NoCodeNoBlunder
我已经更新了我的解决方案。我认为问题在于我在迭代列表时从中删除了一些内容,这导致了一些不良的副作用。 - NoCodeNoBlunder

2
你可以使用 more_itertools.interleave_longest 函数:
>>> from more_itertools import interleave_longest
>>> list(interleave_longest(
...     ['DOG','CAT','BEAR'],
...     ['V','W','X','Y','Z'],
...     ['A','B','C','D','E','F','G','H'],
... ))
['DOG', 'V', 'A', 'CAT', 'W', 'B', 'BEAR', 'X', 'C', 'Y', 'D', 'Z', 'E', 'F', 'G', 'H']

1

编辑:此答案以无序方式合并列表,这不完全是 OP 所想要的。

使用 itertools.chain 将列表链接成一个单一列表,然后使用 random.shuffle 在原地对此列表进行洗牌:

from itertools import chain
from random import shuffle

list_01 = ['DOG','CAT','BEAR']
list_02 = ['V','W','X','Y','Z']
list_03 = ['A','B','C','D','E','F','G','H']

full_list = list(chain(list_01, list_02, list_03))
shuffle(full_list)

print(full_list)  # in my case: ['BEAR', 'V', 'Z', 'W', 'A', 'Y', 'E', 'G', 'CAT', 'B', 'X', 'H', 'D', 'F', 'DOG', 'C']

这个想法不是要洗牌,而是按顺序挑选。 - AKX
@AKX 哦,我明白了,这个问题我没有理解清楚。不确定是否应该删除我的答案,但至少我会编辑并添加一个警告。 - jfaccioni

1
你可以使用zip函数。

list_01 = ['DOG','CAT','BEAR']
list_02 = ['V','W','X','Y','Z']
list_03 = ['A','B','C','D','E','F','G','H']

minimum_size = min(len(list_01) , len(list_02) , len(list_03))

new_array = []

for item1 , item2 , item3 in zip(list_01 , list_02 , list_03):
    new_array.extend([item1 , item2 , item3])
    
new_array += list_01[minimum_size:] + list_02[minimum_size:] + list_03[minimum_size:]

print(new_array)

1
不同长度的列表会导致它无法正确工作。 - eroot163pi

1

通过使用纯Python(不使用numpy),这是我能想到的最好方法。

我认为它适用于任何顺序、列表长度,且不假定填充值。

list_01 = ['DOG','CAT','BEAR']
list_02 = ['V','W','X','Y','Z']
list_03 = ['A','B','C','D','E','F','G','H']
L = [list_01, list_03, list_02]
def zigzag(*arg):
    i = 0
    LL = []
    all_done = False
    while not all_done:
        all_done = True
        for l in arg:
            if i >= len(l):
                continue
            all_done = False
            LL.append(l[i])
        i += 1
    return LL
print(zigzag(list_01, list_02, list_03))
print(zigzag(list_01, list_03, list_02))

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