在每个x位置合并2个列表

时间:2013-06-21 18:38:09

标签: python

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

在python中进行此操作的最佳方法是什么?

8 个答案:

答案 0 :(得分:5)

以下是来自itertools documentation的圆形食谱的改编版本,应该按照您的要求进行:

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))

答案 1 :(得分: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]

答案 2 :(得分: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的所有额外元素只会附加到结尾。

答案 3 :(得分: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 count才能拥有但是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)只取实际值来删除索引......

答案 4 :(得分:0)

以上解决方案真的很酷。这是一个不涉及roundrobin或itertools的替代方案。

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短。

答案 5 :(得分:0)

sep, lst = 2, []
for i in range(len(y)+1):
    lst += x[i*sep:(i+1)*sep] + y[i:i+1]

sep是插入x元素之前y的元素数。

性能:

>>> 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

非常好。我无法让stmtlet = []开头,所以我认为它会继续附加到lst(除非我误解timeit),但仍然......漂亮好一百万次。

答案 6 :(得分: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]

答案 7 :(得分: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'