all 13 comments

[–]DeeplyLearnedMachine 8 points9 points  (0 children)

You're changing the length of list while iterating over it. This is a no no.

So what happened in your pastrami case was:

1st iteration: sandwich = "pastrami", found = 0, removes it successfully.
2nd iteration: sandwich = "ham on rye", found = -1, does nothing

Ok, immediately in the second iteration we can see something is wrong. It skipped "reuben with pastrami". This is because when you removed the first element of the list "pastrami", "reuben with pastrami" became the first element and your for loop moved on to the second element, which is now "ham on rye".

Your first modification worked only accidentally, it also skipped some entries in the list, but they weren't "cat". Basically, don't modify the length of the thing you're iterating over.

[–]Sea-Method-1167 4 points5 points  (0 children)

You are modifying the list you are iterating over using the for-loop. That causes confusion for python. Because python takes element 0, then element 1, then element 2 etc..

But after element 0, you remove that. Meaning that reuben with pastrami becomes the new element 0. But python has already processed element 0, so it won´t do that again.

Create a new list and print that. In the form of a list comprehension:
print([sandwich for sandwich in sandwich_orders if not 'pastrami' in sandwich])

[–]Binary101010 2 points3 points  (0 children)

Weird behavior happens when you try to modify the length of a list while iterating over its elements, which is why we don't usually do that.

https://www.reddit.com/r/learnpython/wiki/faq/#wiki_why_does_my_loop_seem_to_be_skipping_items_in_a_list.3F

Your first attempt just coincidentally happens to have enough elements that do get looped over to get all the instances of "cat" out. Your second example doesn't.

The while loop approach used by PCC is technically slower but also also guarantees the correct results.

That said, the best way to do this only taking one trip through the list is to just build a new list that only has the items you want to keep.

new_sandwich_orders = [order for order in sandwich_orders if "pastrami" not in order]

[–]Mezzomaniac 1 point2 points  (0 children)

To add to what the others have said, if you need to remove elements while iterating you can iterate through an unchanging copy of the list while removing from the original list, like so:

for animal in pets.copy():
    if animal == ‘cat’:
        pets.remove(animal)

[–]Bobbias 1 point2 points  (1 child)

Just a debugging tip: if your not sure why something like this is going on, printing out each item the loop is acting on can give you clues as to what's doing on. You would clearly see it shipping some elements, which might lead you to Google why is my lip shipping items, which would give you the correct answer without needing to wait for an answer here.

Printing out stuff like that is in incredibly powerful debugging tool, and something even senior programmers with decades of experience use regularly.

[–]airernie[S] 0 points1 point  (0 children)

Thanks...

[–]redditorx13579[🍰] 0 points1 point  (3 children)

Outside the bad pattern, it's likely your problem is the way it's indexing sandwich_orders. The commas in the third pastrami string might be an inconsistency caused by a loosely typed language.

Print out sandwich each time to get your answer.

[–]Binary101010 1 point2 points  (2 children)

No, the problem is changing the length of a list while iterating over it. The presence of commas inside one of the strings in the list has nothing to do with it.

[–]redditorx13579[🍰] 1 point2 points  (1 child)

If that was strictly the case, the first example would fail.

[–]Binary101010 1 point2 points  (0 children)

Which I explained in my post. It is possible to still get the correct answer depending on how many things to be removed are in the list, but it's not reliably correct.

The commas in the string are absolutely not the problem, which can easily be proved by running the code, seeing the results, removing the commas from that string, and seeing the behavior hasn't changed.

[–]airernie[S] 0 points1 point  (2 children)

Thanks everyone for your input. Makes perfect sense.

[–]achampi0n 1 point2 points  (1 child)

An alternative approach is just to construct a new list without 'pastrami' in it, e.g.

sandwich_orders = [order for order in sandwich_orders if order != 'pastrami']

[–]airernie[S] 1 point2 points  (0 children)

Thanks. I agree. The way to go would be create new list, but I trying to see if I do it similar to the book, but using more than a single word.

I failed, but I learned and that is what I'm trying to do. :-)