Об'єднати 2 списків у кожній x-позиції

Say I have two lists one longer than the other, x = [1,2,3,4,5,6,7,8] and y = [a,b,c] and I want to merge each element in y to every 3rd index in x so the resulting list z would look like: z = [1,2,a,3,4,b,5,6,c,7,8]

Який би найкращий спосіб це зробити в пітоні?

4

8 Відповіді

Ось пристосована версія рецепта Roundrobin з документації для itertools , яка повинна роби що хочеш:

from itertools import cycle, islice

def merge(a, b, pos):
    "merge('ABCDEF', [1,2,3], 3) --> A B 1 C D 2 E F 3"
    iterables = [iter(a)]*(pos-1) + [iter(b)]
    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))

Приклад:

>>> list(merge(xrange(1, 9), 'abc', 3))   # note that this works for any iterable!
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]

Або ось як ви можете скористатись roundrobin() , оскільки це без будь-яких змін:

>>> x = [1,2,3,4,5,6,7,8]
>>> y = ['a','b','c']
>>> list(roundrobin(*([iter(x)]*2 + [y])))
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]

Або еквівалентна, але трохи більш читабельна версія:

>>> xiter = iter(x)
>>> list(roundrobin(xiter, xiter, y))
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]

Зауважте, що обидва ці методи працюють з будь-якими ітерабельними, а не просто послідовностями.

Ось оригінальна версія roundrobin() :

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))
5
додано

Цей підхід змінює x на своєму місці. Крім того, ви можете зробити копію x і повернути модифіковану копію, якщо ви не хочете змінити оригінал.

def merge(x, y, offset):
    for i, element in enumerate(y, 1):
        x.insert(i * offset - 1, element)

>>> x = [1,2,3,4,5,6,7,8]
>>> y = ['a','b','c']
>>> merge(x, y, 3)
>>> x
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]

Усі додаткові елементи y , що проходять через кінець x , просто додаються до кінця.

3
додано
@ 2rs2ts Погоджено, вставка - O (n) . Для малих наборів даних мій підхід працює, але я б не радив це для великих наборів.
додано Автор Matthew Jacobs, джерело
Я думаю, що це залежить від того, де це використовується. Якщо він знаходиться в середині циклу, який знову і знову викликається, ви хочете, щоб ви скористалися найшвидшим способом. Якщо, однак, це називається один раз (можливо, іноді), сила швидкості може бути надзвичайно важливою. Щоб поставити номер на це, може бути, кілька сотень. Однак, я вважаю, де він використовується більш важливо.
додано Автор Matthew Jacobs, джерело
Мені подобається це просто і лаконічно, і я можу зрозуміти це! Я не можу коментувати ефективність будь-якого рішення, як це було вище мого розуміння на даний момент.
додано Автор KingFu, джерело
@ 2rs2ts добре, що ускладнює речі тоді! Хм, я можу чекати і дозволити більш досвідченим людям голосувати за найкраще (найшвидше?) Рішення, перш ніж я виберу відповідь потім
додано Автор KingFu, джерело
@MatthewJacobs, що б ви вважали великим набором даних?
додано Автор KingFu, джерело
>>> from itertools import chain
def solve(x,y):                                                             
    it = iter(y)
    for i in xrange(0, len(x), 2):
        try:
            yield x[i:i+2] + [next(it)]
        except StopIteration:    
            yield x[i:]
...

>>> x = [1,2,3,4,5,6,7,8]
>>> y = ['a','b','c']

>>> list(chain.from_iterable(solve(x,y)))
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]
3
додано

Ось ще один спосіб:

x = range(1, 9)
y = list('abc')

from itertools import count, izip
from operator import itemgetter
from heapq import merge

print map(itemgetter(1), merge(enumerate(x), izip(count(1, 2), y)))
# [1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]

Це стримує всі ненужні перед створенням нового списку, і дозволяє merge природним чином об'єднати послідовності ... різновид прикрасити/undecorate ... Це вимагає Python 2.7 для count мати аргумент step , хоча.

Отже, пройтися трохи:

a = list(enumerate(x))
# [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8)]
b = zip(count(1, 2), y)
# [(1, 'a'), (3, 'b'), (5, 'c')]
print list(merge(a, b))
# [(0, 1), (1, 2), (1, 'a'), (2, 3), (3, 4), (3, 'b'), (4, 5), (5, 6), (5, 'c'), (6, 7), (7, 8)]

Тоді itemgetter (1) просто приймає фактичне значення видалення індексу ...

3
додано

Вищевказані рішення дійсно круті. Ось альтернатива, яка не включає в себе круговий або ітертол.

def merge(x, y):
    result = []
    while y:
        for i in range(0, 2): result.append(x.pop(0))
        for i in range(0, 1): result.append(y.pop(0))
    result.extend(x)
    return result

де 2 і 1 є довільними, а список y вважається меншим за список x.

0
додано
Це схоже на мою власну реалізацію, за винятком, що я використовував один цикл і умовний. Хоча я підозрюю, що ваша робота може бути кращою
додано Автор KingFu, джерело
sep, lst = 2, []
for i in range(len(y)+1):
    lst += x[i*sep:(i+1)*sep] + y[i:i+1]

Де sep - це кількість елементів x перед вставленням елемента y .

Продуктивність:

>>> timeit.timeit(stmt="for i in range(len(y)+1): lst += x[i*sep:(i+1)*sep] + y[i:i+1]", setup="lst = [];x = [1,2,3,4,5,6,7,8];y = ['a','b','c'];sep = 2", number=1000000)
2.8565280437469482

Красивий damn гарний. Я не зміг отримати stmt , щоб почати з let = [] , тому я думаю, що він зберігає додавання до lst (якщо я не розумію < code> timeit ), але все одно ... гарно вигідно за мільйон разів.

0
додано

Використовуючи itertools.izip_longest :

>>> from itertools import izip_longest, chain
>>> y = ['a','b','c']
>>> x = [1,2,3,4,5,6,7,8]   
>>> lis = (x[i:i+2] for i in xrange(0, len(x) ,2)) # generator expression
>>> list(chain.from_iterable([ (a + [b]) if b else a  
                                            for a, b in izip_longest(lis, y)]))
[1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8]
0
додано
def merge(xs, ys):
    ys = iter(ys)
    for i, x in enumerate(xs, 1):
        yield x
        if i % 2 == 0:
            yield next(ys)

''.join(merge('12345678', 'abc')) # => '12a34b56c78'
0
додано
ІТ КПІ - Python
ІТ КПІ - Python
625 учасників

Канал обговорень про всякі штуки зі світу пайтону. Прохання: 0. мати повагу одне до одного; 1. не матюкатися в сторону людей; 2. не захламляти тред повідомленнями по одному слову;