5

假设我有两个列表,一个比另一个长,x = [1,2,3,4,5,6,7,8]并且y = [a,b,c]我想将 y 中的每个元素合并到 x 中的每个第三个索引,因此结果列表 z 看起来像:z = [1,2,a,3,4,b,5,6,c,7,8]

在 python 中解决这个问题的最佳方法是什么?

4

8 回答 8

5

这是来自itertools 文档的轮询配方的改编版本,应该可以满足您的要求:

from itertools import cycle, islice

def merge(a, b, pos):
    "merge('ABCDEF', [1,2,3], 3) --> A B 1 C D 2 E F 3"
    iterables = [iter(a)]*(pos-1) + [iter(b)]
    pending = len(iterables)
    nexts = cycle(iter(it).next for it in iterables)
    while pending:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            pending -= 1
            nexts = cycle(islice(nexts, pending))

例子:

>>> list(merge(xrange(1, 9), 'abc', 3))   # note that this works for any iterable!
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]

或者,您可以roundrobin()在不进行任何修改的情况下按原样使用:

>>> x = [1,2,3,4,5,6,7,8]
>>> y = ['a','b','c']
>>> list(roundrobin(*([iter(x)]*2 + [y])))
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]

或等效但更具可读性的版本:

>>> xiter = iter(x)
>>> list(roundrobin(xiter, xiter, y))
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]

请注意,这两种方法都适用于任何可迭代的,而不仅仅是序列。

这是原始roundrobin()实现:

from itertools import cycle, islice

def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    # Recipe credited to George Sakkis
    pending = len(iterables)
    nexts = cycle(iter(it).next for it in iterables)
    while pending:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            pending -= 1
            nexts = cycle(islice(nexts, pending))
于 2013-06-21T18:53:07.830 回答
3

这是另一种方式:

x = range(1, 9)
y = list('abc')

from itertools import count, izip
from operator import itemgetter
from heapq import merge

print map(itemgetter(1), merge(enumerate(x), izip(count(1, 2), y)))
# [1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]

这使它在构建新列表之前保持懒惰,并让merge自然地合并序列......有点装饰/不装饰......虽然它确实需要 Python 2.7count有一个step参数。

所以,稍微介绍一下:

a = list(enumerate(x))
# [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8)]
b = zip(count(1, 2), y)
# [(1, 'a'), (3, 'b'), (5, 'c')]
print list(merge(a, b))
# [(0, 1), (1, 2), (1, 'a'), (2, 3), (3, 4), (3, 'b'), (4, 5), (5, 6), (5, 'c'), (6, 7), (7, 8)]

然后itemgetter(1)只取删除索引的实际值......

于 2013-06-21T19:38:06.023 回答
3

这种方法x就地修改。x或者,如果您不想更改原件,您可以复制并返回修改后的副本。

def merge(x, y, offset):
    for i, element in enumerate(y, 1):
        x.insert(i * offset - 1, element)

>>> x = [1,2,3,4,5,6,7,8]
>>> y = ['a','b','c']
>>> merge(x, y, 3)
>>> x
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]

y结束后的所有额外元素都x将附加到末尾。

于 2013-06-21T19:00:04.873 回答
3
>>> from itertools import chain
def solve(x,y):                                                             
    it = iter(y)
    for i in xrange(0, len(x), 2):
        try:
            yield x[i:i+2] + [next(it)]
        except StopIteration:    
            yield x[i:]
...

>>> x = [1,2,3,4,5,6,7,8]
>>> y = ['a','b','c']

>>> list(chain.from_iterable(solve(x,y)))
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]
于 2013-06-21T18:46:52.283 回答
0

使用itertools.izip_longest

>>> from itertools import izip_longest, chain
>>> y = ['a','b','c']
>>> x = [1,2,3,4,5,6,7,8]   
>>> lis = (x[i:i+2] for i in xrange(0, len(x) ,2)) # generator expression
>>> list(chain.from_iterable([ (a + [b]) if b else a  
                                            for a, b in izip_longest(lis, y)]))
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]
于 2013-06-21T19:21:52.467 回答
0
sep, lst = 2, []
for i in range(len(y)+1):
    lst += x[i*sep:(i+1)*sep] + y[i:i+1]

插入元素之前的元素sep数量在哪里。xy

表现:

>>> timeit.timeit(stmt="for i in range(len(y)+1): lst += x[i*sep:(i+1)*sep] + y[i:i+1]", setup="lst = [];x = [1,2,3,4,5,6,7,8];y = ['a','b','c'];sep = 2", number=1000000)
2.8565280437469482

相当不错。我无法stmt开始,let = []所以我认为它一直在附加lst(除非我误解了timeit),但仍然......一百万次相当不错。

于 2013-06-21T19:04:58.113 回答
0

上面的解决方案真的很酷。这是一个不涉及循环或迭代工具的替代方案。

def merge(x, y):
    result = []
    while y:
        for i in range(0, 2): result.append(x.pop(0))
        for i in range(0, 1): result.append(y.pop(0))
    result.extend(x)
    return result

其中 2 和 1 是任意的,并且假定列表 y 比列表 x 短。

于 2013-06-21T19:01:51.133 回答
0
def merge(xs, ys):
    ys = iter(ys)
    for i, x in enumerate(xs, 1):
        yield x
        if i % 2 == 0:
            yield next(ys)

''.join(merge('12345678', 'abc')) # => '12a34b56c78'
于 2013-06-21T19:30:09.847 回答