Double Iteration in List Comprehension

Clash Royale CLAN TAG#URR8PPP
Double Iteration in List Comprehension
In Python you can have multiple iterators in a list comprehension, like
[(x,y) for x in a for y in b]
for some suitable sequences a and b. I'm aware of the nested loop semantics of Python's list comprehensions.
My question is: Can one iterator in the comprehension refer to the other? In other words: Could I have something like this:
[x for x in a for a in b]
where the current value of the outer loop is the iterator of the inner?
As an example, if I have a nested list:
a=[[1,2],[3,4]]
what would the list comprehension expression be to achieve this result:
[1,2,3,4]
?? (Please only list comprehension answers, since this is what I want to find out).
8 Answers
8
To answer your question with your own suggestion:
>>> [x for b in a for x in b] # Works fine
While you asked for list comprehension answers, let me also point out the excellent itertools.chain():
>>> from itertools import chain
>>> list(chain.from_iterable(a))
>>> list(chain(*a)) # If you're using python < 2.6
Gee, I guess I found the anwser: I was not taking care enough about which loop is inner and which is outer. The list comprehension should be like:
[x for b in a for x in b]
to get the desired result, and yes, one current value can be the iterator for the next loop :-). Sorry for the noise.
@Glenn Yeah, it easily gets convoluted for more than simple expressions.
– ThomasH
Jul 29 '09 at 8:53
Ew. I'm not sure this is the "usual" use for list comprehensions, but it's very unfortunate that chaining is so nasty in Python.
– Matt Joiner
Aug 28 '11 at 10:24
It looks very clean if you put newlines before each 'for'.
– Nick Garvey
Sep 11 '14 at 7:03
Wow, this is completely reverse to what makes sense in my head.
– obskyr
Jun 21 '17 at 10:03
I hope this helps someone else since a,b,x,y don't have much meaning to me! Suppose you have a text full of sentences and you want an array of words.
a,b,x,y
# Without list comprehension
list_of_words =
for sentence in text:
for word in sentence:
list_of_words.append(word)
return list_of_words
I like to think of list comprehension as stretching code horizontally.
Try breaking it up into:
# List Comprehension
[word for sentence in text for word in sentence]
Brilliant explanation!
– nekomatic
Dec 16 '16 at 13:05
"There are only two hard problems in Computer Science: cache invalidation and naming things." -- Phil Karlton
– cezar
Aug 8 at 8:42
Order of iterators may seem counter-intuitive.
Take for example: [str(x) for i in range(3) for x in foo(i)]
[str(x) for i in range(3) for x in foo(i)]
Let's decompose it:
def foo(i):
return i, i + 0.5
[str(x)
for i in range(3)
for x in foo(i)
]
# is same as
for i in range(3):
for x in foo(i):
yield str(x)
What an eye-opener !!
– nehemiah
Jun 28 '17 at 3:44
My understanding is that the reason for this is that "the first iteration listed is the topmost iteration that would be typed if the comprehension were written as nested for loops". The reason this is counterintuitive is that the OUTER loop (topmost if written as nested for-loops) appears at the INSIDE of the bracketed list/dict (comprehension'ed object). Conversely, the INNER loop (innermost when written as nested for-loops) is precisely the rightmost loop in a comprehension, and is in that way appears at the OUTSIDE of the comprehension.
– Zach Siegel
Aug 6 '17 at 6:31
ThomasH has already added a good answer, but I want to show what happens:
>>> a = [[1, 2], [3, 4]]
>>> [x for x in b for b in a]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined
>>> [x for b in a for x in b]
[1, 2, 3, 4]
>>> [x for x in b for b in a]
[3, 3, 4, 4]
I guess Python parses the list comprehension from left to right. This means, the first for loop that occurs will be executed first.
for
The second "problem" of this is that b gets "leaked" out of the list comprehension. After the first successful list comprehension b == [3, 4].
b
b == [3, 4]
Interesting point. I was surprised at this:
x = 'hello'; [x for x in xrange(1,5)]; print x # x is now 4– grinch
Nov 18 '14 at 17:11
x = 'hello';
[x for x in xrange(1,5)];
print x # x is now 4
This leakage was fixed in Python 3: stackoverflow.com/questions/4198906/…
– Denilson Sá Maia
Oct 6 '15 at 13:34
If you want to keep the multi dimensional array, one should nest the array brackets. see example below where one is added to every element.
>>> a = [[1, 2], [3, 4]]
>>> [[col +1 for col in row] for row in a]
[[2, 3], [4, 5]]
>>> [col +1 for row in a for col in row]
[2, 3, 4, 5]
I feel this is easier to understand
[row[i] for row in a for i in range(len(a))]
result: [1, 2, 3, 4]
Additionally, you could use just the same variable for the member of the input list which is currently accessed and for the element inside this member. However, this might even make it more (list) incomprehensible.
input = [[1, 2], [3, 4]]
[x for x in input for x in x]
First for x in input is evaluated, leading to one member list of the input, then, Python walks through the second part for x in x during which the x-value is overwritten by the current element it is accessing, then the first x defines what we want to return.
for x in input
for x in x
x
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
List comprehension syntax is not one of Python's shining points.
– Glenn Maynard
Jul 29 '09 at 8:37