Merge 2 lists at each position x

Let's say I have two lists longer than the other, x = [1,2,3,4,5,6,7,8] and y = [a,b,c] , and I want to combine each element from y into each the third index in x, so that the resulting list z looks like this: z = [1,2,a,3,4,b,5,6,c,7,8]

What would be the best way to do this in python?

+4
source share
8 answers

Here is an adapted version of the roundrobin recipe from the itertools documentation that should do what you want:

 from itertools import cycle, islice def merge(a, b, pos): "merge('ABCDEF', [1,2,3], 3) --> AB 1 CD 2 EF 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)) 

Example:

 >>> 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] 

Or here is how roundrobin() can be used, since it is without any changes:

 >>> 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] 

Or an equivalent but slightly more readable version:

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

Note that both of these methods work with any iterative, not just sequences.

Here is the original implementation of roundrobin() :

 from itertools import cycle, islice def roundrobin(*iterables): "roundrobin('ABC', 'D', 'EF') --> ADEBFC" # 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
source
 >>> 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
source

This approach changes x in place. In addition, you can make a copy of x and return the modified copy if you do not want to change the original.

 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] 

All additional elements of y at the end of x simply appended to the end.

+3
source

Here's another way:

 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] 

This allows you to be lazy until you create a new list and allows merge naturally combine sequences ... decoration / undecorate ... For Python 2.7, count requires a step argument.

So, a little walk:

 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)] 

Then itemgetter(1) just takes the actual value, removing the index ...

+3
source

The above solutions are really cool. Here is an alternative that does not include roundrobin or itertools.

 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 

where 2 and 1 are arbitrary and the list y is considered shorter than the list x.

0
source
 sep, lst = 2, [] for i in range(len(y)+1): lst += x[i*sep:(i+1)*sep] + y[i:i+1] 

Where sep is the number of x elements before inserting the y element.

Performance:

 >>> 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 

Pretty damn good. I could not get stmt to start with let = [] , so I think it continued to attach to lst (if I don't understand) timeit ), but still ... pretty good a million times.

0
source

Using 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
source
 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
source

Source: https://habr.com/ru/post/1487536/


All Articles