1

I am basically trying to get my head around using a list comprehension with this basic bit of code. Im trying to duplicate a list item by the value of the list item:

y = [1, 2, 0, 1]
x = []
for i in y:
    for j in range(i):
        x.append(i)
# Desired output
>>> [1, 2, 2, 1]

x = [i for _ in range(i) for i in y]
# Wrong output
>>> [1, 2, 0, 1]

# Right output
x = [j for j in y for _ in range(j)]
>>> [1, 2, 2, 1]

I just cant seem to get my head around why I get the wrong output for the second example. Could someone explain what is wrong here. Thanks.

kezzos
  • 3,023
  • 3
  • 20
  • 37
  • 1
    Your first comprehension `[i for _ in range(i) for i in y]` will give an error on execution(Namely, `name 'i' is not defined`). In nested list comprehensions, loops go from outside in. – Rahul Jun 27 '16 at 12:37
  • 2
    @Rahul It wouldn't raise that error because the `for` loop in the previous lines will leak the value of `i`. – Selcuk Jun 27 '16 at 12:39
  • 1
    Also see http://stackoverflow.com/a/17657966/4014959 – PM 2Ring Jun 27 '16 at 13:03

3 Answers3

3

When you have multiple for loops inside a list comprehension, the loops are processed in the same order as they would be using "traditional" for loops. Your list comp that gives the correct output has the loops in the same order as your code at the start using .append with traditional for loops.

As Rahul mentions in the comments, in isolation,

x = [i for _ in range(i) for i in y]

would give a

NameError: name 'i' is not defined

It doesn't in this case because i was defined by the earlier code.


In contrast, look at what happens with a nested list comp:

y = [1, 2, 0, 1]
x = [[j for j in range(i)] for i in y]
print(x)    

output

[[0], [0, 1], [], [0]]

Here, the outermost loop is in the outer comprehension and the inner loop is in the inner comprehension. So for each iteration of the for i in y we create a new list comp, and in that list comp we loop over range(i).

PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
1

The right comprehension is:

x = [i for i in y for j in range(i)]

This give the result you want
item for item in list range(item) times
inspired by this thread

flattened = [val for sublist in list_of_lists for val in sublist]
Community
  • 1
  • 1
mquantin
  • 1,085
  • 8
  • 23
-1
x = [j for j in y for i in range(j)]
khelili miliana
  • 3,730
  • 2
  • 15
  • 28
  • 1
    This code is a bit confusing because it re-uses `y`. Also, it's always better on SO to add some explanation to your code. Code-only answers with no explanation are of limited usefulness. – PM 2Ring Jun 27 '16 at 12:54