У меня есть два списка, первый из которых гарантированно будет содержать ровно на один элемент больше, чем второй . Я хотел бы знать, какой способ создания нового списка с помощью 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
Как еще этого можно достичь? Какой самый питонический подход?
Ответы:
Вот один из способов сделать это, нарезав:
>>> 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']
источник
Там рецепт для этого в
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))
РЕДАКТИРОВАТЬ:
Для версии 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 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))
источник
zip_longest
.itertools
документации, потому что он.next()
больше не работает.__next__
. Этого нет в документации, поэтому я предложил отредактировать ответ.Это должно делать то, что вы хотите:
>>> iters = [iter(list1), iter(list2)] >>> print list(it.next() for it in itertools.cycle(iters)) ['f', 'hello', 'o', 'world', 'o']
источник
roundrobin
этой ситуации эта функция излишняя.list(itertools.chain(map(next, itertools.cycle(iters)), *iters))
import itertools print [x for x in itertools.chain.from_iterable(itertools.izip_longest(list1,list2)) if x]
Я думаю, что это самый питонический способ сделать это.
источник
None
s, которые * должны быть в списке. Я отредактирую исправленную версию, чтобы исправить этоFalse
, или даже вещи , которые будут только оценены , какFalse
вif
-expression, как, например,0
или пустой список. Это может быть (частично) избежать следующего:[x for x in itertools.chain.from_iterable(itertools.zip_longest(list1, list2)) if x is not None]
. Конечно, это все равно не сработает, если в списках естьNone
элементы, которые необходимо сохранить. В этом случае вам нужно изменитьfillvalue
аргументzip_longest
, как уже предлагал Дабслоу.None
проблема, похоже, исчезла, по крайней мере, с Python 3.7.6 (я не знаю для более старых версий). Еслиalt_chain
определено какdef alt_chain(*iters, fillvalue=None): return chain.from_iterable(zip_longest(*iters, fillvalue=fillvalue))
, тоlist(alt_chain([0, False, 1, set(), 3, 4], [0, None, 1, {}], fillvalue=99))
вернет правильно[0, 0, False, None, 1, 1, set(), {}, 3, 99, 4, 99]
.Без 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')
источник
[(l1[0], l2[0]), (l1[1], l2[1]), ...]
.sum
объединяет кортежи вместе: в(l1[0], l2[0]) + (l1[1], l2[1]) + ...
результате получаются чередующиеся списки. Остальная часть однострочной строки - это просто заполнение l1 дополнительным элементом для работы zip и нарезкой до -1, чтобы избавиться от этого заполнения.filter(None, ...
(может использоватьbool
вместо этого илиNone.__ne__
) удаляет ложные значения, включая 0, None и пустые строки, поэтому второе выражение не является строго эквивалентным первому.sum
это сделали? Какую роль здесь играет второй аргумент? В документации второй аргументstart
.Я знаю, что вопросы касаются двух списков, в одном из которых на один элемент больше, чем в другом, но я решил, что поставлю это для тех, кто может найти этот вопрос.
Вот решение Дункана, адаптированное для работы с двумя списками разного размера.
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']
источник
Если оба списка имеют одинаковую длину, вы можете:
[x for y in zip(list1, list2) for x in y]
Поскольку в первом списке есть еще один элемент, вы можете добавить его постфактум:
[x for y in zip(list1, list2) for x in y] + [list1[-1]]
источник
Вот один лайнер, который это делает:
list3 = [ item for pair in zip(list1, list2 + [0]) for item in pair][:-1]
источник
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
источник
def combine(list1, list2, lst=[]):
, отсюда и мой комментарий. Однако к тому времени, когда я отправил этот комментарий, killown внесла необходимые изменения.Это основано на приведенном выше вкладе Карлоса Валиенте с возможностью чередовать группы из нескольких элементов и убедиться, что все элементы присутствуют на выходе:
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]
источник
Возможно, будет немного поздно купить еще один однострочник на 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']
источник
Мое мнение:
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)]
источник
Вот один лайнер с использованием списков без других библиотек:
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))]
источник
from itertools import chain list(chain(*zip('abc', 'def'))) # Note: this only works for lists of equal length ['a', 'd', 'b', 'e', 'c', 'f']
источник
Остановки по кратчайшему:
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__ может быть быстрее.
источник
Это неприятно, но работает независимо от размера списков:
list3 = [element for element in list(itertools.chain.from_iterable([val for val in itertools.izip_longest(list1, list2)])) if element != None]
источник
Несколько однострочников, вдохновленных ответами на другой вопрос :
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]
источник
Как насчет 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])
источник
Альтернатива функциональным и неизменным способом (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),[])
источник
Я бы сделал простое:
Он предложит итератор, не создавая дополнительных потребностей в хранилище.
источник
chain.from_iterable(izip(list1, list2), list1[len(list2):])
помощью конкретной проблемы, заданной здесь ... list1 должен быть более длинным.Я слишком стар, чтобы отказываться от понимания списков, поэтому:
import operator list3 = reduce(operator.add, zip(list1, list2))
источник