Pythonic способ объединить два списка поочередно? - PullRequest
67 голосов
/ 09 сентября 2010

У меня есть два списка, , первый из которых гарантированно содержит ровно на один элемент больше, чем второй . Я хотел бы знать самый Pythonic способ создать новый список, чьи значения четного индекса приходят из первого списка, а чьи значения нечетного индекса приходят из второго списка.

# 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

Как еще это может быть достигнуто? Какой самый питонский подход?

Ответы [ 18 ]

85 голосов
/ 09 сентября 2010

Вот один из способов сделать это нарезкой:

>>> 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']
46 голосов
/ 09 сентября 2010

Рецепт для этого есть в документации itertools :

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))
27 голосов
/ 09 сентября 2010

Это должно делать то, что вы хотите:

>>> iters = [iter(list1), iter(list2)]
>>> print list(it.next() for it in itertools.cycle(iters))
['f', 'hello', 'o', 'world', 'o']
23 голосов
/ 31 января 2014
import itertools
print [x for x in itertools.chain.from_iterable(itertools.izip_longest(list1,list2)) if x]

Я думаю, что это самый питонский способ сделать это.

12 голосов
/ 10 сентября 2010

Без itertools и при условии, что l1 на 1 единицу длиннее l2:

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

Использование itertools и допущение, что списки не содержат None:

>>> filter(None, sum(itertools.izip_longest(l1, l2), ()))
('f', 'hello', 'o', 'world', 'o')
10 голосов
/ 02 декабря 2012

Я знаю, что вопросы касаются двух списков, в одном из которых один элемент больше другого, но я решил, что я бы поставил его для других, кто может найти этот вопрос.

Вот решение Дункана , приспособленное для работы с двумя списками разных размеров.

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'] 
4 голосов
/ 09 сентября 2010

Вот один лайнер, который делает это:

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

2 голосов
/ 23 октября 2012

Это основано на вкладе Карлоса Вальенте, указанном выше, с возможностью чередовать группы из нескольких элементов и убедиться, что все элементы присутствуют в выходных данных:

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]
1 голос
/ 09 сентября 2010

Мой дубль:

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)]
1 голос
/ 09 сентября 2010

Вот один вкладыш, использующий списки, без других библиотек:

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

Вот другой подход, если вы разрешите изменение вашего исходного списка1 побочным эффектом:

[list1.insert((i+1)*2-1, list2[i]) for i in range(len(list2))]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...