Подтвердить что ты не робот

Объединяйте списки в 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.

4b9b3361

Ответ 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']