-2

Why the below code in where I am using list comprehension doesn't work, but the common way does it?

exams = [1,2,3,1,2,3,4,1,5,1]
repeatedExams = []

# EXCPECTED OUPUT
# repeatedExams = [1,2,3]

# IF I USE repeatedExams.append(i)
repeatedExams = [repeatedExams.append(i) for i in exams if (exams.count(i)>1) and (i not in repeatedExams)]
    # OUTPUT 
    # repeatedExams = [None,None,None]

# IF I ONLY USE i
repeatedExams = [i for i in exams if (exams.count(i)>1) and (i not in repeatedExams)]
    # OUTPUT
    # repeatedExams = [1,2,3,1,2,3,4,1,5,1]

# WORK 
for i in exams:
    if exams.count(i) > 1 and i not in repeatedExams:
        repeatedExams.append(i)

    # OUTPUT
    # repeatedExams = [1,2,3]
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
Juan Rojas
  • 49
  • 5

5 Answers5

0

list.append() returns None

Comprehension returns a collection for you; you do not need to pre-define one

As you are building one, you shouldn't be using it, so not in repeatedExams within the comprehension isn't going to do what you want


If you want to count repeats, use a Counter

from collections import Counter

exams = [1,2,3,1,2,3,4,1,5,1]
c = Counter(exams)
repeatedExams = [k for k,v in c.items() if v > 1]
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
0

If you really want to go with list comprehension, then you need to split the loop into two steps:

# Keep only repetated elements
repeatedExams = [i for i in exams if exams.count(i)>1] 
# Remove duplicates of repeated elements       
repeatedExams = set(repeatedExams)
ibarrond
  • 6,617
  • 4
  • 26
  • 45
0

The list comprehension syntax tells Python to take an iterable, and for each element of that iterable, evaluate an expression, and then build a list out of all the results returned by that expression. append is what's known as an "in place" operator. What that means is that it operates not by returning a result, but by modifying the associated object. The actual value of inplace operators is generally None. For instance, repeatedExams.append(i) modifies the object repeatedExams. The modified version of repeatedExams is now stored where ther original repeatedExams was, and repeatedExams.append(i) returns None. Your syntax then stores all those Nones in a list.

It may be informative to do

for i in exams:
    if (exams.count(i)>1) and (i not in repeatedExams):
        print(repeatedExams.append(i))

and see what you get.

Acccumulation
  • 3,491
  • 1
  • 8
  • 12
0

Try this -

repeatedExams = [i for i in exams if exams.count(i)>1] 
repeatedExams = set(repeatedExams)

Or-

repeatedExams = set([i for i in exams if exams.count(i)>1])
0
repeatedExams = {exam for exam in exams if exams.count(exam)>1}

similar to user16198685's answer, but the [] brackets and 'set()' are redundant

exam for exam in exams if exams.count(exam)>1 is a generator set comprehension takes generators as arguments, as well as listcomps. So set([]) brackets can be replaced with '{}'

mightbesimon
  • 380
  • 3
  • 7