Объединяйте списки в Python, помещая каждый n-й элемент из одного списка и других из другого?
У меня есть два списка, list1
и list2
.
Здесь len(list2) << len(list1)
.
Теперь я хочу объединить оба списка, чтобы каждый n-й элемент конечного списка из list2
и остальных из list1
.
Например:
list1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
list2 = ['x', 'y']
n = 3
Теперь окончательный список должен быть:
['a', 'b', 'x', 'c', 'd', 'y', 'e', 'f', 'g', 'h']
Каков самый Pythonic способ достичь этого?
Я хочу добавить все элементы list2
в окончательный список, окончательный список должен включать все элементы из list1
и list2
.
Ответы
Ответ 1
Создание большего списка итератором позволяет легко брать несколько элементов для каждого элемента меньшего списка:
list1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
list2 = ['x', 'y']
n = 3
iter1 = iter(list1)
res = []
for x in list2:
res.extend([next(iter1) for _ in range(n - 1)])
res.append(x)
res.extend(iter1)
>>> res
['a', 'b', 'x', 'c', 'd', 'y', 'e', 'f', 'g', 'h']
Это позволяет избежать insert
, что может быть дорогостоящим для больших списков, потому что каждый раз, когда весь список необходимо воссоздать.
Ответ 2
Чтобы сохранить исходный список, вы можете попробовать следующее:
result = copy.deepcopy(list1)
index = n - 1
for elem in list2:
result.insert(index, elem)
index += n
Результат
['a', 'b', 'x', 'c', 'd', 'y', 'e', 'f', 'g', 'h']
Ответ 3
Используя модуль itertools
и дополнительный пакет more_itertools
, вы можете построить итеративное решение несколькими разными способами. Сначала импорт:
import itertools as it, more_itertools as mt
Этот первый кажется самым чистым, но он полагается на more_itertools.chunked()
.
it.chain(*mt.roundrobin(mt.chunked(list1, n-1), list2))
В этом используется только more_itertools.roundrobin()
, реализация которого взята из документации itertools
, поэтому, если у вас нет доступа к more_itertools
, вы можете просто скопировать его самостоятельно.
mt.roundrobin(*([iter(list1)]*(n-1) + [list2]))
В качестве альтернативы это делает почти то же самое, что и первый образец без использования каких-либо more_itertools
-специфических функций. В принципе, grouper
может заменить chunked
, но в конце он добавит None
в конец, поэтому я обернул его в it.takewhile
, чтобы удалить их. Естественно, если вы используете это в списках, которые на самом деле содержат None
, он остановится, когда он достигнет этих элементов, поэтому будьте осторожны.
it.takewhile(lambda o: o is not None,
it.chain(*mt.roundrobin(mt.grouper(n-1, list1), list2))
)
Я тестировал их на Python 3.4, но я считаю, что эти образцы кода также должны работать в Python 2.7.
Ответ 4
Как насчет решения ниже? Однако у меня нет лучшего...
>>> list1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
>>> list2 = ['x', 'y']
>>> n = 2
>>> for i in range(len(list2)):
... list1.insert(n, list2[i])
... n += 3
...
...
>>> list1
['a', 'b', 'x', 'c', 'd', 'y', 'e', 'f', 'g', 'h']
n
равно 2, потому что индекс третьего элемента в списке равен 2, так как он начинается с 0.
Ответ 5
list(list1[i-1-min((i-1)//n, len(list2))] if i % n or (i-1)//n >= len(list2) else list2[(i-1)//n] for i in range(1, len(list1)+len(list2)+1))
Определенно не pythonic, но я подумал, что это может быть интересно сделать в однострочном. Более читаемая версия (действительно?):
list(
list1[i-1-min((i-1)//n, len(list2))]
if i % n or (i-1)//n >= len(list2)
else
list2[(i-1)//n]
for i in range(1, len(list1)+len(list2)+1)
)
В принципе, некоторые перебирают индексы и определяют, какой список и какой индекс нужно взять из следующего элемента.
Ответ 6
Еще один способ, вычисляющий шаги среза:
list1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
list2 = ['x', 'y']
n = 3
res = []
m = n - 1
start, end = 0, m
for x in list2:
res.extend(list1[start:end])
res.append(x)
start, end = end, end + m
res.extend(list1[start:])
>>> res
['a', 'b', 'x', 'c', 'd', 'y', 'e', 'f', 'g', 'h']
Ответ 7
list1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
list2 = ['x', 'y']
n = 3
new = list1[:]
for index, item in enumerate(list2):
new[n * (index + 1) - 1: n * (index + 1) - 1] = item
print(new)
Ответ 8
Я восхищаюсь использованием @David Z more_itertools
. Обновления инструментов могут упростить решение:
import more_itertools as mit
n = 3
groups = mit.windowed(list1, n-1, step=n-1)
list(mit.flatten(mit.interleave_longest(groups, list2)))
# ['a', 'b', 'x', 'c', 'd', 'y', 'e', 'f', 'g', 'h']
Сводка: list2
перемежается в группы из list1
и, наконец, сглаживается в один список.
Примечания
-
groups
: n-1
размер скользящих окон, например. [('a', 'b'), ('c', 'd'), ('e', 'f'), ('g', 'h')]
-
interleave_longest
в настоящее время эквивалентен roundrobin
-
None
- значение заполнения по умолчанию. Необязательно удалить с помощью filter(None, ...)
Ответ 9
Может быть, это другое решение, нарисуйте list1
правильный индекс и добавьте элемент list2
в list1
.
>>> list1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
>>> list2 = ['x', 'y']
>>> n = 3
>>> for i in range(len(list2)):
... list1 = list1[:n*(i+1) - 1] + list(list2[i]) + list1[n*(i+1)-1:]
...
>>> list1
['a', 'b', 'x', 'c', 'd', 'y', 'e', 'f', 'g', 'h']