2

I have a list whose nested list's size may vary with the multiple of 2. Currently, in this example, the nested list's length is 4.

a_list = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]

According to length, I am trying to break the list to get the following result in the best possible pythonic way:

a = [[1,2], [5,6], [9,10]]
b = [[3,4], [7,8], [11,12]]

and if nested list's length is 6, then

c = [[..], [..], [..]]

Its kind of a transpose of a nested list but with sets of 2 values in a single row not to be transposed.

Ali Raza Bhayani
  • 3,045
  • 26
  • 20

5 Answers5

6

Using list comprehension:

>>> a_list = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
>>> a = [x[:2] for x in a_list]
>>> b = [x[2:] for x in a_list]
>>> a
[[1, 2], [5, 6], [9, 10]]
>>> b
[[3, 4], [7, 8], [11, 12]]

More general solution:

>>> [[x[i:i+2] for x in a_list] for i in range(0, len(a_list[0]), 2)]
[[[1, 2], [5, 6], [9, 10]],
 [[3, 4], [7, 8], [11, 12]]]
falsetru
  • 357,413
  • 63
  • 732
  • 636
2

I'd hesitate to call this "pythonic", since it's pretty much illegible, but:

>>> a, b = zip(*(zip(*[iter(s)]*2) for s in a_list))
>>> a
((1, 2), (5, 6), (9, 10))
>>> b
((3, 4), (7, 8), (11, 12))

Also works for 6-item lists:

>>> a_list = [[1,2,3,4,100,102],[5,6,7,8,103,104],[9,10,11,12,105,106]]
>>> a, b, c = zip(*(zip(*[iter(s)]*2) for s in a_list))
>>> a
((1, 2), (5, 6), (9, 10))
>>> b
((3, 4), (7, 8), (11, 12))
>>> c
((100, 102), (103, 104), (105, 106))
Zero Piraeus
  • 56,143
  • 27
  • 150
  • 160
  • 1
    +1 for `zip(*[iter(s)]*2)`, I've never seen this idiom :-) I _would_ call your answer pythonic: Iterator solutions are pythonic, and this has fewer components than the slice solution-- as long as you speak iterator. – alexis Sep 29 '14 at 15:46
  • For the general-purpose solution, one can simply turn the result of `zip` into a list (in python 2 it already is a list) and save it in a variable. – alexis Sep 29 '14 at 15:51
1

Almost the same as falsetru's answer, but first the nested lists are split into chunks of size 2 and then all of them are zipped together.

>>> a_list = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
>>> zip(*([j[i*2: i*2 + 2] for i in range(len(j) / 2)] for j in a_list))
[([1, 2], [5, 6], [9, 10]), ([3, 4], [7, 8], [11, 12])]

>>> a_list = [[1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]]
>>> zip(*([j[i*2: i*2 + 2] for i in range(len(j) / 2)] for j in a_list))
[([1, 2], [7, 8]), ([3, 4], [9, 10]), ([5, 6], [11, 12])]

>>> a_list = [[1,2,3,4,100,102],[5,6,7,8,103,104],[9,10,11,12,105,106]]
>>> zip(*([j[i*2: i*2 + 2] for i in range(len(j) / 2)] for j in a_list))
[([1, 2], [5, 6], [9, 10]), ([3, 4], [7, 8], [11, 12]), ([100, 102], [103, 104], [105, 106])]
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
1

A Fast way is using numpy.hsplit :

>>> import numpy
>>> numpy.hsplit(numpy.array(a_list),2)
[array([[ 1,  2],[ 5,  6],[ 9, 10]]),array([[ 3,  4],[ 7,  8],[11, 12]])]
Mazdak
  • 105,000
  • 18
  • 159
  • 188
1

Since readability is pythonic, here's a simpler iterator-based solution (without the neat tricks that @Zero used to turn it into a one-liner):

First, an iterator that turns a list [1,2,3,4,5,6] into [(1, 2), (3, 4), (5, 6)].

def pairs(lst):
    it=iter(lst)
    return list(zip(it, it))   # Return a list of pairs drawn from the same iterator

The list a_list can be transformed into a list of such pair lists as follows:

a_list = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]    
pair_list = [ pairs(row) for row in a_list ]

Finally, we need to effectively transpose this list, making a list of the first pair/element from each sublist, another list of the second one, etc. A nice idiom for transposing a list is zip(*some_list). Let's use it to make the transformation requested by the OP:

a, b = zip(*pair_list)

or to collect any number of generated lists in one list:

results = list( zip(*pair_list) )

Feel free to pack these into a one-liner (though I wouldn't):

results = list(zip( *(pairs(row) for row in a_list) ))
Community
  • 1
  • 1
alexis
  • 48,685
  • 16
  • 101
  • 161