Pythonic способ комбинировать два списка в чередующемся режиме?
У меня есть два списка, , первый из которых, как гарантируется, содержит ровно еще один элемент, чем второй. Я хотел бы знать самый 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
Как еще это можно достичь? Какой самый пифонический подход?
Ответы
Ответ 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
Вот рецепт этого в 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))
Ответ 3
Это должно делать то, что вы хотите:
>>> iters = [iter(list1), iter(list2)]
>>> print list(it.next() for it in itertools.cycle(iters))
['f', 'hello', 'o', 'world', 'o']
Ответ 4
import itertools
print [x for x in itertools.chain.from_iterable(itertools.izip_longest(list1,list2)) if x]
Я думаю, что это самый питонический способ сделать это.
Ответ 5
Без 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')
Ответ 6
Я знаю, что вопросы задают два списка, один из которых имеет один элемент больше, чем другой, но я подумал, что поставлю это для других, кто может найти этот вопрос.
Вот решение 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']
Ответ 7
Вот один лайнер, который делает это:
list3 = [ item for pair in zip(list1, list2 + [0]) for item in pair][:-1]
Ответ 8
Это основано на вкладе Карлоса Вальенте выше
с возможностью чередования групп из нескольких элементов и убедитесь, что все элементы присутствуют в выводе:
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]
Ответ 9
Здесь один лайнер с использованием списков, без других библиотек:
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))]
Ответ 10
Мой прием:
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)]
Ответ 11
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
Ответ 12
Может быть немного поздно, купить еще один 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']
Ответ 13
Остановка кратчайшего:
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__ может быть быстрее.
Ответ 14
Это неприятно, но работает независимо от размера списков:
list3 = [element for element in list(itertools.chain.from_iterable([val for val in itertools.izip_longest(list1, list2)])) if element != None]
Ответ 15
Несколько одностроек, вдохновленных ответами на другой вопрос:
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]
Ответ 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
Альтернатива в функционале & неизменный путь (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),[])
Ответ 18
Я бы сделал простой:
chain.from_iterable( izip( list1, list2 ) )
Он найдет итератор без каких-либо дополнительных потребностей в хранении.
Ответ 19
Я слишком стар, чтобы быть со списком, поэтому:
import operator
list3 = reduce(operator.add, zip(list1, list2))