3

Object is a decoded json object that contains a list called items.

obj = json.loads(response.body_as_unicode())

for index, item in enumerate(obj['items']):
   if not item['name']:
       obj['items'].pop(index)

I iterate over those items and want to remove an item when a certain condition is met. However this is not working as expected. After some research I found out that one cannot remove items from a list while at the same time iterating of this list in python. But I cannot apply the mentioned solutions to my problem. I tried some different approaches like

obj = json.loads(response.body_as_unicode())
items = obj['items'][:]

for index, item in enumerate(obj['items']):
   if not item['name']:
       obj['items'].remove(item)

But this removes all items instead of just the one not having a name. Any ideas how to solve this?

DarkLeafyGreen
  • 69,338
  • 131
  • 383
  • 601

2 Answers2

14

Don't remove items from a list while iterating over it; iteration will skip items as the iteration index is not updated to account for elements removed.

Instead, rebuild the list minus the items you want removed, with a list comprehension with a filter:

obj['items'] = [item for item in obj['items'] if item['name']]

or create a copy of the list first to iterate over, so that removing won't alter iteration:

for item in obj['items'][:]:  # [:] creates a copy
   if not item['name']:
       obj['items'].remove(item)

You did create a copy, but then ignored that copy by looping over the list that you are deleting from still.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
6

Use a while loop and change the iterator as you need it:

obj = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# remove all items that are smaller than 5
index = 0
# while index in range(len(obj)): improved according to comment
while index < len(obj):
    if obj[index] < 5:
        obj.pop(index)
        # do not increase the index here
    else:
        index = index + 1

print obj

Note that in a for loop the iteration variable cannot be changed. It will always be set to the next value in the iteration range. Therefore the problem is not the enumerate function but the for loop.

And in the future please provide a verifiable example. Using a json object in the example is not sensible because we do not have this object.

Michael Westwort
  • 914
  • 12
  • 24
  • Why the very verbose and (and especially on Python 2, very costly) `index in range(len(obj))`? Why not just `index < len(obj)`? – Martijn Pieters Jul 23 '16 at 23:10
  • 1
    I would say that is a detail in the question. The essential parts are the difference between while and for loops and the handling of the iterator of the while loop when list items are popped. However you are right: Your suggestion improves that detail. – Michael Westwort Jul 23 '16 at 23:18