Discussion:
[Tutor] Help understanding list comprehensions
Anubhav Yadav
2015-06-16 15:45:01 UTC
Permalink
I have a doubt.

I had a list like this

[['Varun', 19.0], ['Kakunami', 19.0], ['Harsh', 20.0], ['Beria', 20.0],
['Vikas', 21.0]]

I am using a for loop to del the rows with the lowest value like this:

for row in marks:
if row[1] == lowest:
marks.remove(row)

But after running this loop, I get the following:

[['Kakunami', 19.0], ['Harsh', 20.0], ['Beria', 20.0], ['Vikas', 21.0]]

If I print row for every iteration one row is missed in the iteration. Here
is the output.

['Varun', 19.0]
['Harsh', 20.0]
['Beria', 20.0]
['Vikas', 21.0]

If I use list comprehensions, I get the right output. I just want to
understand what is happening. Can anyone help me out here?

This if from an hackerrank assignment.
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Alan Gauld
2015-06-16 18:17:37 UTC
Permalink
Post by Anubhav Yadav
I have a doubt.
So have I. I can't se your code so I don't know what's happening.
Don't make us guess, show us the code.
Post by Anubhav Yadav
I had a list like this
[['Varun', 19.0], ['Kakunami', 19.0], ['Harsh', 20.0], ['Beria', 20.0],
['Vikas', 21.0]]
But its not assigned to anything?
Or did you really define it by assigning it to a
variable - marks maybe? If so show us the code.
Post by Anubhav Yadav
marks.remove(row)
What is 'lowest'? You don;t define it anywhere so you
should get an error. Or maybe you do but you haven't
shown us?
Post by Anubhav Yadav
[['Kakunami', 19.0], ['Harsh', 20.0], ['Beria', 20.0], ['Vikas', 21.0]]
So it deleted Varun.
I'm guessing but what happened next was that the list counter now
pointed at index 1, but with Varun gone that is now Harsh, so it
never tests Kakunami. Removing elements from the thing you are iterating
over is like cutting off the tree branch you are
standing on - a bad idea.

Either take a copy of the list or use a while loop...
Post by Anubhav Yadav
If I print row for every iteration one row is missed in the iteration. Here
is the output.
['Varun', 19.0]
['Harsh', 20.0]
['Beria', 20.0]
['Vikas', 21.0]
Which is very odd because its a different set from the previous 'result'
above. But maybe if you showed us the code we could
understand it.
Post by Anubhav Yadav
If I use list comprehensions, I get the right output. I just want to
understand what is happening. Can anyone help me out here?
How do you use the list comprehension(s)? Shjow us the code
and we might see why there is a difference. Don't make us guess.
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Válas Péter
2015-06-16 17:44:42 UTC
Permalink
What is the exact meaning of "marks" and "lowest" in your script? Where are
they defined?0
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Alan Gauld
2015-06-16 23:00:55 UTC
Permalink
I am sorry I didn't posted my code. I had solved this question long
back on hackerrank, and I couldn't figure out why this behaviour was
seen in this problem, so I had posted this question on their
discussion forum. Couldn't get a good answer so I decided to post
here. Luckily I found the revision history of my submission there
where I could find the earlier code which was giving this error.
Here is the code.
marks = [['Varun', 19.0], ['Kakunami', 19.0], ['Harsh', 20.0],
['Beria', 20.0], ['Vikas', 21.0]]
marks = sorted(marks, key=lambda score:score[1])
lowest = marks[0][1]
marks.remove(row)
I already explained why this messes up, don;t mutate the thing
you are iterating over.
second_lowest = marks[0][1]
This doesn't work because the loop didn't work.
sh = []
sh.append(row)
This could be a comprehension if you wanted

sh = [ row for row in marks if second_lowest == row[1] ]

But you haven't shown where you printed the 'output' that
was wrong, nor have you shown the comprehensions which
'worked'.

As it is you need to fix the first for loop before anything
else can work as you want it to.
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos

_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Válas Péter
2015-06-17 04:18:19 UTC
Permalink
Either the subject is misleading or you misunderstand something. Im am
sorry to tell you the great truth, but there was no list comprehension in
your code at all, just a list. Comprehension is what Alan wrote for you,
that is the next step in studying Python, when you already understand lists
and loops well.
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Anubhav Yadav
2015-06-17 08:58:54 UTC
Permalink
Either the subject is misleading or you misunderstand something. Im am
sorry to tell you the great truth, but there was no list comprehension in
your code at all, just a list. Comprehension is what Alan wrote for you,
that is the next step in studying Python, when you already understand lists
and loops well.


I understand that my title was a little misleading, and I apologise for the
same. Here is code that worked:

marks = []
for i in range(int(input())):
name = raw_input()
score = float(raw_input())
marks.append([name, score])
marks = sorted(marks, key=lambda score:score[1])
lowest = marks[0][1]
marks = [ x for x in marks if x[1] != lowest]
second_lowest = marks[0][1]
lowest = sorted([x for x in marks if x[1]==second_lowest])
for row in lowest:
print row[0]

And it worked because I was using list comprehensions for removing the
element from the list. It didn't worked in the for loop before. I wanted to
ask why it didn't worked in for loop but worked in list comprehension.
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Laura Creighton
2015-06-17 11:28:38 UTC
Permalink
Post by Válas Péter
Either the subject is misleading or you misunderstand something. Im am
sorry to tell you the great truth, but there was no list comprehension in
your code at all, just a list. Comprehension is what Alan wrote for you,
that is the next step in studying Python, when you already understand lists
and loops well.
I understand that my title was a little misleading, and I apologise for the
marks = []
name = raw_input()
score = float(raw_input())
marks.append([name, score])
marks = sorted(marks, key=lambda score:score[1])
lowest = marks[0][1]
marks = [ x for x in marks if x[1] != lowest]
second_lowest = marks[0][1]
lowest = sorted([x for x in marks if x[1]==second_lowest])
print row[0]
And it worked because I was using list comprehensions for removing the
element from the list. It didn't worked in the for loop before. I wanted to
ask why it didn't worked in for loop but worked in list comprehension.
You have this problem.
Type "help", "copyright", "credits" or "license" for more information.
Post by Válas Péter
mylist = [1,2,3]
... print i
... mylist.remove(i)
...
1
3
Post by Válas Péter
mylist
[2]

--------
you didn't want to skip over 2.

--------
So do this instead.
Post by Válas Péter
from copy import copy
mylist=[1,2,3]
mylist_copy=copy(mylist)
... print i
... mylist.remove(i)
...
1
2
3
Post by Válas Péter
mylist
[]
Post by Válas Péter
mylist_copy
[1, 2, 3]

------------

Much better. :) This goes to show that you shouldn't delete items out
of a list you are interating over, unless you want the weird output
you got above. But even if you did -- it will confuse the heck out
of the next person to read your code, which may be you in six months
time, so even then it might be better to rewrite the thing.

Make a copy of the list instead. Now, python gives you lots of ways to
make copies. One way is to use the copy command, as I did. This has
the advantage that everybody knows exactly what it is that you did.
It has the disadvantage that people have to type 'from copy import copy'
and some people -- I am not one of them -- find it too much typing.
Post by Válas Péter
mylist=[1,2,3]
mylist_copy=mylist[:]
mylist
[1, 2, 3]
Post by Válas Péter
mylist_copy
[1, 2, 3]

and since this way of doing things is so very well known, you mostly don't
confuse people by copying that way.

And, as you may have found out in code not here, you can use a
list comprehension to construct a whole new list, so again you
wouldn't have the problem of deleting items out of a list you
are iterating over, because it is a different list.
Post by Válas Péter
And it worked because I was using list comprehensions for removing the
element from the list. It didn't worked in the for loop before. I wanted to
ask why it didn't worked in for loop but worked in list comprehension.
(at least with respect to that code). In the code you posted you didn't
remove anything from the list at all.

Laura
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Alan Gauld
2015-06-17 15:22:13 UTC
Permalink
Post by Anubhav Yadav
I understand that my title was a little misleading, and I apologise for the
marks = []
Don't use input(). Use raw_input() instead.
You almost never want input() in python 2.
Post by Anubhav Yadav
name = raw_input()
score = float(raw_input())
marks.append([name, score])
marks = sorted(marks, key=lambda score:score[1])
lowest = marks[0][1]
marks = [ x for x in marks if x[1] != lowest]
This creates a brand new list. It eventually overwrites
the original marks list with the new list. But it never
modifies the original marks, it reassigns the new list
to the old variable.
Post by Anubhav Yadav
second_lowest = marks[0][1]
lowest = sorted([x for x in marks if x[1]==second_lowest])
print row[0]
And it worked because I was using list comprehensions for removing the
element from the list. It didn't worked in the for loop before. I wanted to
ask why it didn't worked in for loop but worked in list comprehension.
Because the comprehension creates a new list from the old.
You were modifying the original list as you traversed it,
thus breaking your own for loop.
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Anubhav Yadav
2015-06-18 04:40:05 UTC
Permalink
So I need to create a copy of the list before trying to iterate through the
list.
Thanks a lot everyone for their answers.

Cheers.
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Alan Gauld
2015-06-18 08:20:44 UTC
Permalink
Post by Anubhav Yadav
So I need to create a copy of the list before trying to iterate through the
list.
Only if you are going to change it.
The problem you had was that you were removing items
from the thing you were iterating over. As a result
the other items effectively moved up one place to fill
the gap resulting in you stepping over some items without
processing them.

Similarly problems can ensue if you insert new items
into a list while iterating over it. You may wind up
processing some items twice while not processing the
new items you added. It's just a bad idea to modify
the structure of the thing you are iterating over.

So, if you intend modifying the list structure make a
copy. Otherwise you can iterate over the original list
(or dictionary, or whatever) without problems.
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Laura Creighton
2015-06-18 22:20:57 UTC
Permalink
Post by Anubhav Yadav
So I need to create a copy of the list before trying to iterate through the
list.
Thanks a lot everyone for their answers.
Er, I think you understand, but what you need to do is to make a copy
of the list, and _interate through the copy_ if you want to remove
things from your list. If you aren't modifying the list in the
loop/function/method/whatever then you don't need a copy.

I think you already got it, but "what you said" != "what you meant". :)

Laura


_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor

Continue reading on narkive:
Loading...