以 Python 的方式交替组合(交错、交织、缠绕)两个列表?

2024-12-09 08:30:00
admin
原创
81
摘要:问题描述:我有两个列表,保证第一个列表包含的项目恰好比第二个列表多一个。我想知道最符合 Python 风格的方法来创建一个新列表,其偶数索引值来自第一个列表,其奇数索引值来自第二个列表。# example inputs list1 = ['f', 'o', 'o'] list2 = ['hello', 'wo...

问题描述:

我有两个列表,保证第一个列表包含的项目恰好比第二个列表多一个。我想知道最符合 Python 风格的方法来创建一个新列表,其偶数索引值来自第一个列表,其奇数索引值来自第二个列表。

# example inputs
list1 = ['f', 'o', 'o']
list2 = ['hello', 'world']

# desired output
['f', 'hello', 'o', 'world', 'o']

这可行,但并不美观:

list3 = []
while True:
    try:
        list3.append(list1.pop(0))
        list3.append(list2.pop(0))
    except IndexError:
        break

还有什么其他方法可以实现这一点?最符合 Python 风格的方法是什么?


如果您需要处理长度不匹配的列表(例如,第二个列表更长,或者第一个列表比第二个列表多一个元素),这里的一些解决方案将起作用,而其他解决方案则需要调整。有关更具体的答案,请参阅如何交错两个不同长度的列表?以将多余的元素留在末尾,或如何优雅地交错两个长度不均匀的列表?以尝试均匀地散布元素,或在每个第 n 个元素后插入 Python 列表中的元素,以应对特定数量的元素应该在每个“添加”元素之前的情况。


解决方案 1:

以下是通过切片实现此目的的一种方法:

>>> list1 = ['f', 'o', 'o']
>>> list2 = ['hello', 'world']
>>> result = [None]*(len(list1)+len(list2))
>>> result[::2] = list1
>>> result[1::2] = list2
>>> result
['f', 'hello', 'o', 'world', 'o']

解决方案 2:

import itertools

print(
    [x for x in itertools.chain.from_iterable(itertools.zip_longest(list1, list2)) if x]
)

我认为这是最符合 Python 风格的做法。

解决方案 3:

itertools文档中有一个关于此问题的答案(注意:针对 Python 3):

from itertools import cycle, islice

def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    # Recipe credited to George Sakkis
    num_active = len(iterables)
    nexts = cycle(iter(it).__next__ for it in iterables)
    while num_active:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            # Remove the iterator we just exhausted from the cycle.
            num_active -= 1
            nexts = cycle(islice(nexts, num_active))

解决方案 4:

在 Python 2 中,这应该可以完成您想要的操作:

>>> iters = [iter(list1), iter(list2)]
>>> print list(it.next() for it in itertools.cycle(iters))
['f', 'hello', 'o', 'world', 'o']

解决方案 5:

如果两个列表的长度相等,则可以执行以下操作:

[x for y in zip(list1, list2) for x in y]

由于第一个列表还有一个元素,因此您可以事后添加它:

[x for y in zip(list1, list2) for x in y] + [list1[-1]]

编辑:为了说明在第一个列表推导中发生的情况,您可以这样将其拼写为嵌套的 for 循环:

result = []
for y in zip(list1, list2): # y is is a 2-tuple, containining one element from each list
    for x in y: # iterate over the 2-tuple
        result.append(x) # append each element individually

解决方案 6:

没有使用 itertools 并假设 l1 比 l2 长 1 个项目:

>>> sum(zip(l1, l2+[0]), ())[:-1]
('f', 'hello', 'o', 'world', 'o')

在 python 2 中,使用 itertools 并假设列表不包含 None :

>>> filter(None, sum(itertools.izip_longest(l1, l2), ()))
('f', 'hello', 'o', 'world', 'o')

解决方案 7:

我知道问题询问两个列表,其中一个列表比另一个列表多一个项目,但我想我会把它放在其他可能发现这个问题的人面前。

这是Duncan 的解决方案,适用于两个不同大小的列表。

list1 = ['f', 'o', 'o', 'b', 'a', 'r']
list2 = ['hello', 'world']
num = min(len(list1), len(list2))
result = [None]*(num*2)
result[::2] = list1[:num]
result[1::2] = list2[:num]
result.extend(list1[num:])
result.extend(list2[num:])
result

输出:

['f', 'hello', 'o', 'world', 'o', 'b', 'a', 'r'] 

解决方案 8:

下面是一个可以完成此操作的语句:

list3 = [ item for pair in zip(list1, list2 + [0]) for item in pair][:-1]

解决方案 9:

这是一个使用列表推导的单行代码,没有其他库:

list3 = [sub[i] for i in range(len(list2)) for sub in [list1, list2]] + [list1[-1]]

以下是另一种方法,如果您允许通过副作用改变初始 list1:

[list1.insert((i+1)*2-1, list2[i]) for i in range(len(list2))]

解决方案 10:

这个是基于 Carlos Valiente 的上述贡献,其中包含一个选项,可以交替分组多个项目,并确保所有项目都存在于输出中:

A=["a","b","c","d"]
B=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]

def cyclemix(xs, ys, n=1):
    for p in range(0,int((len(ys)+len(xs))/n)):
        for g in range(0,min(len(ys),n)):
            yield ys[0]
            ys.append(ys.pop(0))
        for g in range(0,min(len(xs),n)):
            yield xs[0]
            xs.append(xs.pop(0))

print [x for x in cyclemix(A, B, 3)]

这将使列表 A 和 B 按每组 3 个值交错:

['a', 'b', 'c', 1, 2, 3, 'd', 'a', 'b', 4, 5, 6, 'c', 'd', 'a', 7, 8, 9, 'b', 'c', 'd', 10, 11, 12, 'a', 'b', 'c', 13, 14, 15]

解决方案 11:

再买一个 Python 单行代码可能有点晚了。当两个列表的大小相等或不相等时,这种方法有效。值得一提的是,它会修改 a 和 b。如果这是个问题,您需要使用其他解决方案。

a = ['f', 'o', 'o']
b = ['hello', 'world']
sum([[a.pop(0), b.pop(0)] for i in range(min(len(a), len(b)))],[])+a+b
['f', 'hello', 'o', 'world', 'o']

解决方案 12:

from itertools import chain
list(chain(*zip('abc', 'def')))  # Note: this only works for lists of equal length
['a', 'd', 'b', 'e', 'c', 'f']

解决方案 13:

itertools.zip_longest返回一个元组对的迭代器,其中一个列表中任何缺失的元素都将被替换为fillvalue=None(传递fillvalue=object可让您将其用作None值)。如果您展平这些对,然后fillvalue在列表推导中进行过滤,则得到:

>>> from itertools import zip_longest
>>> def merge(a, b):
...     return [
...         x for y in zip_longest(a, b, fillvalue=object)
...         for x in y if x is not object
...     ]
...
>>> merge("abc", "defgh")
['a', 'd', 'b', 'e', 'c', 'f', 'g', 'h']
>>> merge([0, 1, 2], [4])
[0, 4, 1, 2]
>>> merge([0, 1, 2], [4, 5, 6, 7, 8])
[0, 4, 1, 5, 2, 6, 7, 8]

推广至任意可迭代对象:

>>> def merge(*its):
...     return [
...         x for y in zip_longest(*its, fillvalue=object)
...         for x in y if x is not object
...     ]
...
>>> merge("abc", "lmn1234", "xyz9", [None])
['a', 'l', 'x', None, 'b', 'm', 'y', 'c', 'n', 'z', '1', '9', '2', '3', '4']
>>> merge(*["abc", "x"]) # unpack an iterable
['a', 'x', 'b', 'c']

最后,您可能希望返回一个生成器而不是列表推导:

>>> def merge(*its):
...     return (
...         x for y in zip_longest(*its, fillvalue=object)
...         for x in y if x is not object
...     )
...
>>> merge([1], [], [2, 3, 4])
<generator object merge.<locals>.<genexpr> at 0x000001996B466740>
>>> next(merge([1], [], [2, 3, 4]))
1
>>> list(merge([1], [], [2, 3, 4]))
[1, 2, 3, 4]

如果您对其他软件包没有异议,您可以尝试more_itertools.roundrobin

>>> list(roundrobin('ABC', 'D', 'EF'))
['A', 'D', 'E', 'B', 'F', 'C']

解决方案 14:

我的看法:

a = "hlowrd"
b = "el ol"

def func(xs, ys):
    ys = iter(ys)
    for x in xs:
        yield x
        yield ys.next()

print [x for x in func(a, b)]

解决方案 15:

def combine(list1, list2):
    lst = []
    len1 = len(list1)
    len2 = len(list2)

    for index in range( max(len1, len2) ):
        if index+1 <= len1:
            lst += [list1[index]]

        if index+1 <= len2:
            lst += [list2[index]]

    return lst

解决方案 16:

那 numpy 呢?它也适用于字符串:

import numpy as np

np.array([[a,b] for a,b in zip([1,2,3],[2,3,4,5,6])]).ravel()

结果:

array([1, 2, 2, 3, 3, 4])

解决方案 17:

最短停靠点:

def interlace(*iters, next = next) -> collections.Iterable:
    """
    interlace(i1, i2, ..., in) -> (
        i1-0, i2-0, ..., in-0,
        i1-1, i2-1, ..., in-1,
        .
        .
        .
        i1-n, i2-n, ..., in-n,
    )
    """
    return map(next, cycle([iter(x) for x in iters]))

当然,解决 next/__next__ 方法可能会更快。

解决方案 18:

受另一个问题的答案启发的多个单行代码:

import itertools

list(itertools.chain.from_iterable(itertools.izip_longest(list1, list2, fillvalue=object)))[:-1]

[i for l in itertools.izip_longest(list1, list2, fillvalue=object) for i in l if i is not object]

[item for sublist in map(None, list1, list2) for item in sublist][:-1]

解决方案 19:

一种功能性和不可变性的替代方案(Python 3):

from itertools import zip_longest
from functools import reduce

reduce(lambda lst, zipped: [*lst, *zipped] if zipped[1] != None else [*lst, zipped[0]], zip_longest(list1, list2),[])

解决方案 20:

使用 for 循环我们也可以轻松实现这一点:

list1 = ['f', 'o', 'o']
list2 = ['hello', 'world']
list3 = []

for i in range(len(list1)):
    #print(list3)
    list3.append(list1[i])
    if i < len(list2):
        list3.append(list2[i])
        
print(list3)

输出 :

['f', 'hello', 'o', 'world', 'o']

通过使用列表推导,可以进一步减少这种情况。但为了理解,可以使用这个循环。

解决方案 21:

我的方法如下:

from itertools import chain, zip_longest

def intersperse(*iterators):
    # A random object not occurring in the iterators
    filler = object()

    r = (x for x in chain.from_iterable(zip_longest(*iterators, fillvalue=filler)) if x is not filler)

    return r

list1 = ['f', 'o', 'o']
list2 = ['hello', 'world']

print(list(intersperse(list1, list2)))

它适用于任意数量的迭代器并产生一个迭代器,因此我list()在打印行中应用它。

解决方案 22:

def alternate_elements(small_list, big_list):
mew = []
count = 0
for i in range(len(small_list)):
    mew.append(small_list[i])
    mew.append(big_list[i])
    count +=1
return mew+big_list[count:]

if len(l2)>len(l1):
    res = alternate_elements(l1,l2)
else:
    res = alternate_elements(l2,l1)

print(res)

在这里,我们根据大小和性能交换列表,有人可以提供时间复杂度为 O(len(l1)+len(l2)) 的更好的解决方案吗?

解决方案 23:

如果两个列表的长度相同,我认为最优雅的方法如下:

list(sum(list(zip(list1, list2)), ()))

它甚至可以推广到非等长列表:

list(sum(list(zip(list1, list2)), ())) + \nmax(list1, list2, key=len)[min(len(list1), len(list2)):]

解决方案 24:

我会做简单的事:

chain.from_iterable( izip( list1, list2 ) )

它将产生一个迭代器,而不会产生任何额外的存储需求。

解决方案 25:

这很糟糕,但无论列表大小都可以起作用:

list3 = [
    element for element in 
    list(itertools.chain.from_iterable([
        val for val in itertools.izip_longest(list1, list2)
    ]))
    if element != None
]

解决方案 26:

显然已经迟到了,但是这里有一个简洁的等长列表:

output = [e for sub in zip(list1,list2) for e in sub]

它也适用于任意数量的等长列表:

output = [e for sub in zip(list1,list2,list3) for e in sub]

ETC。

解决方案 27:

我太老了,无法理解列表推导,所以:

import operator
list3 = reduce(operator.add, zip(list1, list2))
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   990  
  在项目管理领域,CDCP(Certified Data Center Professional)认证评审是一个至关重要的环节,它不仅验证了项目团队的专业能力,还直接关系到项目的成功与否。在这一评审过程中,沟通技巧的运用至关重要。有效的沟通不仅能够确保信息的准确传递,还能增强团队协作,提升评审效率。本文将深入探讨CDCP...
华为IPD流程   26  
  IPD(Integrated Product Development,集成产品开发)是一种以客户需求为核心、跨部门协同的产品开发模式,旨在通过高效的资源整合和流程优化,提升产品开发的成功率和市场竞争力。在IPD培训课程中,掌握关键成功因素是确保团队能够有效实施这一模式的核心。以下将从五个关键成功因素展开讨论,帮助企业和...
IPD项目流程图   27  
  华为IPD(Integrated Product Development,集成产品开发)流程是华为公司在其全球化进程中逐步构建和完善的一套高效产品开发管理体系。这一流程不仅帮助华为在技术创新和产品交付上实现了质的飞跃,还为其在全球市场中赢得了显著的竞争优势。IPD的核心在于通过跨部门协作、阶段性评审和市场需求驱动,确保...
华为IPD   26  
  华为作为全球领先的通信技术解决方案提供商,其成功的背后离不开一套成熟的管理体系——集成产品开发(IPD)。IPD不仅是一种产品开发流程,更是一种系统化的管理思想,它通过跨职能团队的协作、阶段评审机制和市场需求驱动的开发模式,帮助华为在全球市场中脱颖而出。从最初的国内市场到如今的全球化布局,华为的IPD体系在多个领域展现...
IPD管理流程   53  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用