# Better way to swap elements in a list?

## Question:

I have a bunch of lists that look like this one:

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

I want to swap elements as follows:

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

The size of the lists may vary, but they will always contain an even number of elements.

I’m fairly new to Python and am currently doing it like this:

``````l =  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
final_l = []
for i in range(0, len(l)/2):
final_l.append(l[2*i+1])
final_l.append(l[2*i])
``````

I know this isn’t really Pythonic and would like to use something more efficient. Maybe a list comprehension?

I don’t see anything wrong with your implementation at all. But you could perhaps do a simple swap instead.

``````l =  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in range(0, len(l), 2):
old = l[i]
l[i] = l[i+1]
l[i+1] = old
``````

EDIT
Apparently, Python has a nicer way to do a swap which would make the code like this

``````l =  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in range(0, len(l), 2):
l[i], l[i+1] = l[i+1], l[i]
``````

Here a single list comprehension that does the trick:

``````In : l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

In : [l[i^1] for i in range(len(l))]
Out: [2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
``````

The key to understanding it is the following demonstration of how it permutes the list indices:

``````In : [i^1 for i in range(10)]
Out: [1, 0, 3, 2, 5, 4, 7, 6, 9, 8]
``````

The `^` is the exclusive or operator. All that `i^1` does is flip the least-significant bit of `i`, effectively swapping 0 with 1, 2 with 3 and so on.

You can use the pairwise iteration and chaining to flatten the list:

``````>>> from itertools import chain
>>>
>>> list(chain(*zip(l[1::2], l[0::2])))
[2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
``````

Or, you can use the `itertools.chain.from_iterable()` to avoid the extra unpacking:

``````>>> list(chain.from_iterable(zip(l[1::2], l[0::2])))
[2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
``````
``````newList = [(x[2*i+1], x[2*i]) for i in range(0, len(x)/2)]
``````

Now find a way to unzip the tuples. I won’t do all of your homework.

Another way, create nested lists with pairs reversing their order, then flatten the lists with `itertools.chain.from_iterable`

``````>>> from itertools import chain
>>> l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(chain.from_iterable([[l[i+1],l[i]] for i in range(0,(len(l)-1),2)]))
[2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
``````

EDIT: I just applied Kasramvd’s benchmark test to my solution and I found this solution is slower than the other top answers, so I wouldn’t recommend it for large lists. I still find this quite readable though if performance is not critical.

Here a solution based in the `modulo` operator:

``````l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even = []
uneven = []
for i,item in enumerate(l):
if i % 2 == 0:
even.append(item)
else:
uneven.append(item)

list(itertools.chain.from_iterable(zip(uneven, even)))
``````

No need for complicated logic, simply rearrange the list with slicing and step:

``````In : l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

In : l[::2], l[1::2] = l[1::2], l[::2]

In : l
Out: [2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
``````

## TLDR;

Edited with explanation

I believe most viewers are already familiar with list slicing and multiple assignment. In case you don’t I will try my best to explain what’s going on (hope I do not make it worse).

To understand list slicing, here already has an excellent answer and explanation of list slice notation.
Simply put:

``````a[start:end] # items start through end-1
a[start:]    # items start through the rest of the array
a[:end]      # items from the beginning through end-1
a[:]         # a copy of the whole array

There is also the step value, which can be used with any of the above:

a[start:end:step] # start through not past end, by step
``````

Let’s look at OP’s requirements:

`````` [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]  # list l
^  ^  ^  ^  ^  ^  ^  ^  ^  ^
0  1  2  3  4  5  6  7  8  9    # respective index of the elements
l  l  l  l  l      # first tier : start=0, step=2
l  l  l  l  l   # second tier: start=1, step=2
-----------------------------------------------------------------------
l  l  l  l  l
l  l  l  l  l   # desired output
``````

First tier will be: `l[::2] = [1, 3, 5, 7, 9]`
Second tier will be: `l[1::2] = [2, 4, 6, 8, 10]`

As we want to re-assign `first = second` & `second = first`, we can use multiple assignment, and update the original list in place:

``````first , second  = second , first
``````

that is:

``````l[::2], l[1::2] = l[1::2], l[::2]
``````

As a side note, to get a new list but not altering original `l`, we can assign a new list from `l`, and perform above, that is:

``````n = l[:]  # assign n as a copy of l (without [:], n still points to l)
n[::2], n[1::2] = n[1::2], n[::2]
``````

Hopefully I do not confuse any of you with this added explanation. If it does, please help update mine and make it better 🙂

One of the possible answer using `chain` and `list comprehension`

``````>>> l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(chain([(l[2*i+1], l[2*i]) for i in range(0, len(l)/2)]))
[(2, 1), (4, 3), (6, 5), (8, 7), (10, 9)]
``````

Another approach with simply re-assigning and slicing technique

``````l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for a in range(0,len(l),2):
l[a:a+2] = l[a-len(l)+1:a-1-len(l):-1]
print l
``````

output

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

## A benchmark between top answers:

Python 2.7:

``````('inp1 ->', 15.302665948867798) # NPE's answer
('inp2a ->', 10.626379013061523) # alecxe's answer with chain
('inp2b ->', 9.739919185638428) # alecxe's answer with chain.from_iterable
('inp3 ->', 2.6654279232025146) # Anzel's answer
``````

Python 3.4:

``````inp1 -> 7.913498195000102
inp2a -> 9.680125927000518
inp2b -> 4.728151862000232
inp3 -> 3.1804273489997286
``````

If you are curious about the different performances between python 2 and 3, here are the reasons:

As you can see @NPE’s answer (`inp1`) performs very better in python3.4, the reason is that in python3.X `range()` is a smart object and doesn’t preserve all the items between that range in memory like a list.

In many ways the object returned by `range()` behaves as if it is a list, but in fact it isn’t. It is an object which returns the successive items of the desired sequence when you iterate over it, but it doesn’t really make the list, thus saving space.

And that’s why in python 3 it doesn’t return a list while you slice the range object.

``````# python2.7
>>> range(10)[2:5]
[2, 3, 4]
# python 3.X
>>> range(10)[2:5]
range(2, 5)
``````

The second significant change is performance accretion of the third approach (`inp3`). As you can see the difference between it and the last solution has decreased to ~2sec (from ~7sec). The reason is because of the `zip()` function which in python3.X it returns an iterator which produces the items on demand. And since the `chain.from_iterable()` needs to iterate over the items once again it’s completely redundant to do it before that too (what that `zip` does in python 2).

Code:

``````from timeit import timeit

inp1 = """
[l[i^1] for i in range(len(l))]
"""
inp2a = """
list(chain(*zip(l[1::2], l[0::2])))
"""
inp2b = """
list(chain.from_iterable(zip(l[1::2], l[0::2])))
"""
inp3 = """
l[::2], l[1::2] = l[1::2], l[::2]
"""

lst = list(range(100000))
print('inp1 ->', timeit(stmt=inp1,
number=1000,
setup="l={}".format(lst)))
print('inp2a ->', timeit(stmt=inp2a,
number=1000,
setup="l={}; from itertools import chain".format(lst)))
print('inp2b ->', timeit(stmt=inp2b,
number=1000,
setup="l={}; from itertools import chain".format(lst)))
print('inp3 ->', timeit(stmt=inp3,
number=1000,
setup="l={}".format(lst)))
``````

For fun, if we interpret “swap” to mean “reverse” in a more general scope, the `itertools.chain.from_iterable` approach can be used for subsequences of longer lengths.

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

def chunk(list_, n):
return (list_[i:i+n] for i in range(0, len(list_), n))

list(chain.from_iterable(reversed(c) for c in chunk(l, 4)))
# [4, 3, 2, 1, 8, 7, 6, 5, 10, 9]
``````

An(other) alternative:

``````final_l = list() # make an empty list
for i in range(len(l)): # for as many items there are in the original list
if i % 2 == 0: # if the item is even
final_l.append(l[i+1]) # make this item in the new list equal to the next in the original list
else: # else, so when the item is uneven
final_l.append(l[i-1]) # make this item in the new list equal to the previous in the original list
``````

This assumes that the original list has an even number of items. If not, a try-except can be added:

``````final_l = list()
for i in range(len(l)):
if i % 2 == 0:
try: # try if we can add the next item
final_l.append(l[i+1])
except:  # if we can't (because i+1 doesnt exist), add the current item
final_l.append(l[i])
else:
final_l.append(l[i-1])
``````

A way using Numpy

``````import numpy as np

l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
l = np.array(l)
final_l = list(np.flip(l.reshape(len(l)//2,2), 1).flatten())
``````

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

``````lst = []
for index in range(len(swap)):
if index%2 == 0 and index < len(swap)-1:
swap[index],swap[index+1]  = swap[index+1],swap[index]
lst.append(swap[index])
print(lst)

out = [1, 2, 3, 4, 5]
``````
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.