1
# D. Given a list of numbers, return a list where
# all adjacent == elements have been reduced to a single element,
# so [1, 2, 2, 3] returns [1, 2, 3]. You may create a new list or
# modify the passed in list.


def remove_adjacent(nums):
  previous =''
  for i in nums:
    if i == previous:
      nums.remove(i)
    else:
      previous = i
  return nums

Hi can someone explain why my code doesn't work. It outputs [2,3,3] instead of [2,3] for the input [2,2,3,3,3]

Xuan
  • 101
  • 9
  • Duplicate of https://stackoverflow.com/questions/1207406/remove-items-from-a-list-while-iterating – Błotosmętek Jul 01 '17 at 07:30
  • Duplicate of https://stackoverflow.com/questions/3460161/remove-adjacent-duplicate-elements-from-a-list – badiya Jul 01 '17 at 07:57
  • Possible duplicate of [Remove items from a list while iterating](https://stackoverflow.com/questions/1207406/remove-items-from-a-list-while-iterating) –  Jul 01 '17 at 09:42

2 Answers2

3

The problem arises when you remove items from a list you are iterating over. Iteration works like this: each index in a list is visited once, from beginning to end. When you delete something, the contents of the list are shifted left:

| 1 | 2 | 3 | 4 | 5 | << your list
| 1 | 2 |   | 4 | 5 | << 3 is deleted
| 1 | 2 | 4 | 5 | << the list fills in the gap

However, the index of the iterator doesn't know the rest of the list has changed one index to the left, so it advances.

Normally, this is how a list iterates:

| 1 | 2 | 3 | 4 | 5 | 
      ^ < index of iterator
| 1 | 2 | 3 | 4 | 5 |
          ^ The index advances, all is good.

When you delete terms, though, this is what happens:

| 1 | 2 | 3 | 4 | 5 |
      ^ index
| 1 | 2 | 4 | 5 | 
          ^ Oops! The iterator skips over the missing 3, and never gets to it.

You can fix this in your by manually iterating through the indices, only advancing if a deletion was not made:

def remove_adjacent(nums):
    previous = ''
    i = 0;
    while i < len(nums):
        if nums[i] == previous:
            nums.remove(i)
        else:
            previous = nums[i]
            i+=1
    return nums

This avoids having to make a soft copy of the list with [:] (although that is another approach)

Hope this helps!

Timothy Kanarsky
  • 419
  • 1
  • 3
  • 5
1

Take a copy of the list and iterate over it first else the iteration will fail with unexpected results

def remove_adjacent(nums):
    previous = ''
    for i in nums[:]: # using the copy of nums
        if i == previous:
            nums.remove(i)
        else:
            previous = i
    return nums

>>> remove_adjacent([2,2,3,3,3])
[2, 3]
pramod
  • 1,453
  • 1
  • 14
  • 16
  • Hi thanks for the answer, do you mind explaining why slicing creates a copy of the list? Also what is the purpose of using a copy instead of the original list – Xuan Jul 01 '17 at 07:54
  • here we are iterating over the copy, so when you modify the original list, you do not modify the copy that you iterate over. – pramod Jul 01 '17 at 09:13