-1
student = [['benny', 12.09], ['harry', 40.03], ['jenny', 56.03], ['garry', 56.33]]
for items in student:
    for _ in range(len(items)):
        if items[1] not in allscores:
          allscores.append(items[1])
print allscores

Output of the code above is

[12.09, 40.03, 56.03, 56.33]

as expected.

But if I turn the 2 for loops to a list comprehension,

allscores = [items[1] for _ in range(len(items)) if items[1] not in allscores for items in student]
print allscores

I get:

allscores = [items[1] for _ in range(len(items)) if items[1] not in allscores for items in student]
NameError: name 'items' is not defined

What is wrong in my list comprehension?

Sel_Python
  • 191
  • 2
  • 7
  • 16
  • https://stackoverflow.com/questions/18072759/list-comprehension-on-a-nested-list/45079294#45079294 – Rahul Sep 04 '18 at 04:41
  • 1
    `allscores = list(set([items[1] for items in student]))` You don't need to have a nested for loop if you already know the index of what you're looking for – shahbazkhan Sep 04 '18 at 04:44
  • 1
    Why do you have nested loops in the first place? There's no point in repeating the `items[1] not in allscores` test multiple times. – molbdnilo Sep 04 '18 at 05:04

1 Answers1

3

The reason that you get the error is that loops in a double comprehension like that are written in the same order as a for loop. In particular,

a = []
for y in z:
    for x in y:
        a.append(x)

could be rewritten as

a = [x for y in z for x in y]

NOT as

a = [x for x in y for y in z]

The syntax you are probably thinking of is for a nested comprehension:

a = [[x for x in y] for y in z]

But that creates a 2D list, which is definitely not what you want here.

Now to answer your actual question, let's start with items[1] for _ in range(len(items)). You are literally checking items[1] twice for each element in your list. There may have been an attempt to ensure that the sub-list has two elements, but you killed it when you explicitly indexed element 1.

Now you'd think that you can truncate your comprehension to

allscores = [items[1] for items in students if items[1] not in allscores]

But you can't. Remember that until the comprehension completes, allscores is undefined. Pre-initializing it to an empty list won't help either because it will stay empty until the comprehension completes.

The solution is to use a set. It's much more efficient than the linear lookup you are trying to do every time with the in operator in if items[1] not in allscores. It can also be used with a generator expression instead of a list comprehension to avoid storing the unnecessary elements:

allscores = set(items[1] for items in students)

Notice the lack of surrounding square brackets. If you absolutely need a list, do

allscores = list(set(items[1] for items in students))
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264