twk
twk

Reputation: 1841

Given an odd length list of values in Python, how can I swap all values other than the final value in the list?

In regards to Python 2.7.12 (disclaimer: I understand Python2 is being phased out to Python3, but the course I'm taking started us here, perhaps to understand older code bases):

I have a list of integers whom I'd like to swap each with their neighboring value. So far, this works great for lists that are even in the number of integers they contain, however when the list length is odd, it's not so easy to simply swap each value, as the number of integers is uneven.

Giving the following code example, how can I swap all values other than the final value in the list?

arr = [1, 2, 3, 4, 5]

def swapListPairs(arr):
    for idx, val in enumerate(arr):
        if len(arr) % 2 == 0:
            arr[idx], arr[val] =  arr[val], arr[idx]  # traditional swap using evaluation order
        else:
            arr[0], arr[1] = arr[1], arr[0]  # this line is not the solution but where I know I need some conditions to swap all list values other than len(arr)-1, but am not sure how to do this?
    return arr

print swapListPairs(arr)

Bonus Points to the ultimate Pythonic Master: How can this code be modified to also swap strings? Right now, I can only use this function using integers and am very curious how I can make this work for both int and str objects?

Thank you so greatly for any insight or suggestions to point me in the right direction! Everyone's help at times here has been invaluable and I thank you for reading and for your help!

Upvotes: 4

Views: 1627

Answers (5)

Copperfield
Copperfield

Reputation: 8510

looks like : and :: confuse you a little, let me explain it:

Sequences type of object in python such as list, tuple, str, among other provide what is know as a slice, it come in 2 flavors:

Slicing: a[i:j] selects all items with index k such that i <= k < j. When used as an expression, a slice is a sequence of the same type. This implies that the index set is renumbered so that it starts at 0.

Extended slicing with a third “step” parameter: a[i:j:k] selects all items of a with index x where x = i + n*k, n >= 0 and i <= x < j.

In both cases i, j and/or k can be omitted and in that case suitable values are used instead

Some examples

>>> arr = [10, 20, 30, 40, 50, 60, 70]
>>> arr[:]
[10, 20, 30, 40, 50, 60, 70]
>>> arr[:3]
[10, 20, 30]
>>> arr[1:3]
[20, 30]
>>> arr[1::2]
[20, 40, 60]
>>> arr[::2]
[10, 30, 50, 70]
>>> 

the working of this can also be illustrated with the following function

def the_slice(lista, ini=None, end=None, step=None):
    result=[]
    if ini is None:
        ini = 0
    if end is None:
        end = len(lista)
    if step is None:
        step = 1
    for index in range(ini,end,step):
        result.append( lista[index] )
    return result



>>> the_slice(arr,step=2) # arr[::2]
[10, 30, 50, 70]
>>> the_slice(arr,ini=1,step=2) # arr[1::2]
[20, 40, 60]
>>> 

Upvotes: 1

Stefan Pochmann
Stefan Pochmann

Reputation: 28596

Similar to @user2357112 but I prefer it this way:

arr[1::2], arr[:-1:2] = arr[:-1:2], arr[1::2]

Demo:

>>> arr = [1, 2, 3, 4, 5]
>>> arr[1::2], arr[:-1:2] = arr[:-1:2], arr[1::2]
>>> arr
[2, 1, 4, 3, 5]

>>> arr = [1, 2, 3, 4, 5, 6]
>>> arr[1::2], arr[:-1:2] = arr[:-1:2], arr[1::2]
>>> arr
[2, 1, 4, 3, 6, 5]

Upvotes: 2

Ted Klein Bergman
Ted Klein Bergman

Reputation: 9746

You could iterate through the length of the list with a step of two and try to swap values (and except index errors).

def swap_list_pairs(arr):
    for index in range(0, len(arr), 2):
        try:
            arr[index], arr[index+1] = arr[index+1], arr[index]
        except IndexError:
            pass
    return arr

This will work for all data types.

As Copperfield suggested, you could get rid of the try-except-clause:

def swap_list_pairs(arr):
    for index in range(1, len(arr), 2):
        arr[index-1], arr[index] = arr[index], arr[index-1]
    return arr

Upvotes: 4

user2357112
user2357112

Reputation: 280291

Here's a shorter, probably faster way based on slice assignment:

def swap_adjacent_elements(l):
    end = len(l) - len(l) % 2
    l[:end:2], l[1:end:2] = l[1:end:2], l[:end:2]

The slice assignment selects the elements of l at all even indices (l[:end:2]) or all odd indices (l[1:end:2]) up to and excluding index end, then uses the same kind of swapping technique you're already using to swap the slices.

end = len(l) - len(l) % 2 selects the index at which to stop. We set end to the closest even number less than or equal to len(l) by subtracting len(l) % 2, the remainder when len(l) is divided by 2.

Alternatively, we could have done end = len(l) & ~1, using bitwise operations. That would construct an integer to use as a mask (~1), with a 0 in the 1 bit and 1s everywhere else, then apply the mask (with &) to set the 1 bit of len(l) to 0 to produce end.

Upvotes: 6

zwol
zwol

Reputation: 140495

This is easier to do without enumerate. Note that it never, ever makes decisions based on the contents of arr; that is what makes it work on anything, not just a pre-sorted list of integers starting from 1.

for i in range(len(arr)//2):
    a = 2*i
    b = a+1
    if b < len(arr):
        arr[a], arr[b] = arr[b], arr[a]

Exercise for you: is the if actually necessary? Why or why not?

Upvotes: 4

Related Questions