Understanding nested list comprehension

PythonNestedList Comprehension

Python Problem Overview


I want to understand nested list comprehension. Below, I listed a list comprehension expression and their for loop equivalent.
I wonder if my understanding is correct on those.

For example,

[(min([row[i] for row in rows]),max([row[i] for row in rows])) 
for i in range(len(rows[0]))]

is equivalent to

result=[]
for i in range(len(rows[0])):
  innerResult=[]
  for row in rows:
    innerResult.append(row[i])
  innerResult2=[]
  for row in rows:
    innerResult2.append(row[i])
  tuple=(min(innerResult), max(innerResult2))
  result.append(tuple)

If I may generalize, I guess

[exp2([exp1 for x in xSet]) for y in ySet]

form can be translated to the following. (I hope I'm correct on this)

result=[]
for y in ySet:
  innerResult =[]
  for x in xSet:
    innerResult.append(exp1)
  exp2Result = exp2(innerResult)
  result.append(exp2Result)

For simpler case,

[exp1 for x in xSet for y in ySet] 

is equal to

result=[] 
for x in xSet:
  for y in ySet: 
    result.append(exp1)

whereas,

[[exp1 for x in xSet] for y in ySet]

is equal to

result=[]
for y in ySet:
  innerResult=[]
  for x in xSet:
    innerResult.append(exp1)
  result.append(innerResult)

I asked a similar question on https://stackoverflow.com/questions/8047965/equivalent-for-loop-expression-for-complex-list-comprehension
The answers given there reconstruct the form after understanding what it does internally.
I'd like to know how it works systematically so I can apply the concept to other slightly varying examples.

Python Solutions


Solution 1 - Python

Indeed, you are correct. This is described in detail in the Expressions section in the Python Language Reference.

Note especially the order of nesting of several fors in a single list comprehension, which is always left-to-right:

>>> matrix = [[1, 2], [3, 4]]
>>> [item for item in row for row in matrix] # oops!
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    [item for item in row for row in matrix]
NameError: name 'row' is not defined
>>> [item for row in matrix for item in row] # nesting is in left-to-right order
[1, 2, 3, 4]

Solution 2 - Python

The short answer is: yes, you are correct in your understanding.

There's only a catch: the way you normally use nested list comprehension in python code is to operate on multidimensional arrays.

A typical example is when you operate on matrices:

>>> matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> [[el - 1 for el in row] for row in matrix]
[[0, 1, 2], [3, 4, 5], [6, 7, 8]]

As you can see the "nesting" works by operating on each dimension of the matrix.

In the examples you provided, it seems that ySet [unfortunate name btw, as sets are one of the types provided with python] is just a generic counter, which makes a bit harder to follow what is going on under the hood.

As for your first example:

>>> rows = ([1, 2, 3], [10, 20, 30])
>>> [(min([row[i] for row in rows]),max([row[i] for row in rows])) for i in range(len(rows[0]))]
[(1, 10), (2, 20), (3, 30)]

You might wish to look into the zip built-in function:

>>> zip(rows[0], rows[1])
[(1, 10), (2, 20), (3, 30)]

or for maximum brevity and elegance:

>>> zip(*rows)
[(1, 10), (2, 20), (3, 30)]

HTH!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioneugeneView Question on Stackoverflow
Solution 1 - PythontaleinatView Answer on Stackoverflow
Solution 2 - PythonmacView Answer on Stackoverflow