James Sapam
James Sapam

Reputation: 16940

Merge two list by selecting 2 element each

Here is the two lists that i want to merge and create a new list:

>>>
>>> ls1 = [1, 2, 3, 4, 5]
>>> ls2 = ['a', 'b', 'c', 'd']  
>>>

NB: the two list could be of different size.

Required output:

[(1, 2, 'a', 'b'), (3, 4, 'c', 'd'), (5)]

I tried using itertools.izip_longest but not able to achieve it:

>>> list(itertools.izip_longest(ls1, ls2))
[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, None)]
>>>

Could some-one throw me some light on it.

Thanks

Upvotes: 3

Views: 102

Answers (3)

Padraic Cunningham
Padraic Cunningham

Reputation: 180481

If you don't care about having a few extra None's, you can use iter with izip_longest to avoid slicing and creating new lists:

ls1 = [1, 2, 3, 4, 5]
ls2 = ['a', 'b', 'c', 'd']

it1 = iter(ls1)
it2 = iter(ls2)

zipped = izip_longest(it1, it1, it2, it2)

print(list(zipped))
[(1, 2, 'a', 'b'), (3, 4, 'c', 'd'), (5, None, None, None)]

Or use filter to remove any None's from the last tuple:

from itertools import izip_longest

ls1 = [1, 2, 3, 4, 5]
ls2 = ['a', 'b', 'c', 'd']

it1 = iter(ls1)
it2 = iter(ls2
zipped = list(izip_longest(it1, it1, it2, it2))
zipped[-1] = tuple(filter(lambda x: x is not None, zipped[-1]))
[(1, 2, 'a', 'b'), (3, 4, 'c', 'd'), (5,)]

For large input you can see izip is quite a bit more efficient:

In [36]: ls1 = [1, 2, 3, 4, 5] * 1000000
In [37]: ls2 = ['a', 'b', 'c', 'd'] * 1000000
In [38]: %%timeit                       
    it1 = iter(ls1)
    it2 = iter(ls2)
    zipped = list(izip_longest(it1, it1, it2, it2))
    zipped[-1] = tuple(filter(lambda x: x is not None, zipped[-1]))
   ....: 
1 loops, best of 3: 224 ms per loop

In [39]: %%timeit result = []
for i in xrange(0, max(len(ls1),len(ls2)), 2):
    result.append(tuple(ls1[i:i+2] + ls2[i:i+2]))
   ....: 
1 loops, best of 3: 1.46 s per loop

In [40]: timeit  list(itertools.izip_longest(ls1[0::2], ls1[1::2], ls2[0::2], ls2[1::2]))
1 loops, best of 3: 404 ms per loop

iter(ls1) creates an iterator so passing it1, it1 means we will pair every two elements from the list, internally python is basically moving a pointer to the next element every time we iterate over it1.

In [9]: ls2 = ['a', 'b', 'c', 'd']    
In [10]: it2 = iter(ls2)    
In [11]: next(it2), next(it2) # get first two elements
Out[11]: ('a', 'b')    
In [12]: next(it2), next(it2) # again call next twice to get the 3rd and 4th elements
Out[12]: ('c', 'd')

Upvotes: 1

timgeb
timgeb

Reputation: 78750

How about a good old for loop?

result = []
for i in range(0, max(len(ls1),len(ls2)), 2):
    result.append(tuple(ls1[i:i+2] + ls2[i:i+2]))

Upvotes: 4

Bill Lynch
Bill Lynch

Reputation: 81996

You'll need to fix the last entry (but you can do that fairly easily because you know the length of ls1 and ls2).

>>> import itertools
>>> ls1 = [1, 2, 3, 4, 5]
>>> ls2 = ['a', 'b', 'c', 'd']  
>>> list(itertools.izip_longest(ls1[0::2], ls1[1::2], ls2[0::2], ls2[1::2]))
[(1, 2, 'a', 'b'), (3, 4, 'c', 'd'), (5, None, None, None)]

Upvotes: 3

Related Questions