Ad

Python: For-in Loop Is Not Checking The Last String In A List, Why?

- 1 answer

I'm using a for-in loop to remove any strings from a list titled "words" that start with "x" as part of a function, but find that this loop will not check the last string in the list. Why is this?

After adding some print statements to figure out where things were going wrong I narrowed it down to the second for-in loop, but beyond that I'm not sure what to do...

def front_x(words):
  print '\n'
  words.sort()
  print words
  words2 = []
  for string in words:
    if string[0] == 'x':
      words2.append(string)
      #print 'added ' + string + ' to words2'
    #else:
      #print '(append)checked ' + string
  for string in words:
    if string[0] == 'x':
      words.remove(string)
      print 'removed ' + string
    else: print 'checked ' + string
  words2.extend(words)
  return words2

As you can see, in each case it will check all of the elements in the list printed above except for the last. Below that are what my program got vs what it is supposed to get.

['axx', 'bbb', 'ccc', 'xaa', 'xzz']
checked axx
checked bbb
checked ccc
removed xaa
X  got: ['xaa', 'xzz', 'axx', 'bbb', 'ccc', 'xzz'] 
expected: ['xaa', 'xzz', 'axx', 'bbb', 'ccc']


['aaa', 'bbb', 'ccc', 'xaa', 'xcc']
checked aaa
checked bbb
checked ccc
removed xaa
X  got: ['xaa', 'xcc', 'aaa', 'bbb', 'ccc', 'xcc'] 
expected: ['xaa', 'xcc', 'aaa', 'bbb', 'ccc']


['aardvark', 'apple', 'mix', 'xanadu', 'xyz']
checked aardvark
checked apple
checked mix
removed xanadu
X  got: ['xanadu', 'xyz', 'aardvark', 'apple', 'mix', 'xyz']
expected: ['xanadu', 'xyz', 'aardvark', 'apple', 'mix']
Ad

Answer

You are mutating the list as you are iterating over it. Behind the scenes Python is stepping through the numeric index of each item in the list. When you remove an item, all of the items with a higher index are shifted.

Instead, build an list of the indices you want to remove, then remove them. Or use a list comprehension to build a new list.

def front_x(words):
    words2 = [w for w in words if w.startswith('x')]
    return words2

If you want to also mutate the original list (modify words) with the function you can do so using:

def drop_front_x(words):
    words2 = []
    indices = [i for i, w in enumerate(words) if w.startswith('x')]
    for ix in reversed(indices):
        words2.insert(0, words.pop(ix))
    return words2
Ad
source: stackoverflow.com
Ad