Search code examples
pythonsortingswap

Python list swap only working on some iterations


I'm having trouble with a swapping problem in Python - I want to sort a list of integers by iterating through it and swapping any value that is out of place with the value that should be in that place. I know the input will be an unsorted list of consecutive integers starting from 1, e.g. [4, 3, 1, 2]

sorted = [4, 3, 1, 2]
swapCount = 0
for i in range(len(sorted)):
  if sorted[i] != i + 1:
    print(f"Before swap {i + 1}: {sorted}")
    sorted[i], sorted[sorted.index(i + 1)] = sorted[sorted.index(i + 1)], sorted[i]
    swapCount += 1
    print(f"After swap {i + 1}: {sorted}")
    
print(swapCount)
# Before swap 1: [4, 3, 1, 2]
# After swap 1: [4, 3, 1, 2]
# Before swap 2: [4, 3, 1, 2]
# After swap 2: [4, 3, 1, 2]
# Before swap 3: [4, 3, 1, 2]
# After swap 3: [4, 1, 3, 2]
# Before swap 4: [4, 1, 3, 2]
# After swap 4: [2, 1, 3, 4]
# 4

So in my loop, the swap doesn't work (or works in some special cases), while the same syntax works outside a loop:

sorted = [4, 3, 1, 2, 5]
print(sorted)
sorted[0], sorted[2] = sorted[2], sorted[0]
print(sorted)
# [4, 3, 1, 2, 5]
# [1, 3, 4, 2, 5]

Can anyone help me understand what's going wrong in the loop?


Solution

  • An assignment like a, b = c, d first uses the right-hand side to define a tuple. Then the first element is assigned to a, and then the second element is assigned to b. The first assignment may include a side effect that could affect the second; they do not happened simultaneously or independently.

    Your code

    sorted[i], sorted[sorted.index(i + 1)] = sorted[sorted.index(i + 1)], sorted[i]
    

    is equivalent to

    t = sorted[sorted.index(i + 1)], sorted[i]
    sorted[i] = t[0]
    sorted[sorted.index(i+1)] = t[1]
    

    Note, in particular, that between the two calls to sorted.index is an assignment that changes sorted, and thus could cause the second sorted.index to return a different value than the first.

    You want to call sorted.index(i+1) first and save its value, then use the saved value in the swap.

    j = sorted.index(i+1)
    sorted[i], sorted[j] = sorted[j], sorted[i]