Reputation: 3086
Given the following list:
my_list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
I want to be able to swap the sub-list my_list[2:4]
with the sub-list my_list[7:10]
as quickly and as efficiently as possible, to get the new list:
new_list=[0, 1, 7, 8, 9, 4, 5, 6, 2, 3, 10, 11, 12]
Here's my attempt:
def swap(s1, s2, l):
seg1=l[:s1.start]+l[s2]
seg2=l[s1.stop : s2.start]
seg3=l[s1]+l[s2.stop:]
return seg1+seg2+seg3
print swap(slice(2,4), slice(7,10), [0,1,2,3,4,5,6,7,8,9,10,11,12])
This does print the desired output, although this way of doing it looks awful to me.
Is there a more easy and elegant way of doing it, that will not create four new lists for every function call? (I plan to call this function a lot)
I don't mind (actually I'd prefer) changing the original list, rather than creating new instance every function call.
Upvotes: 21
Views: 2829
Reputation: 18940
Slices can be assigned.
Two variables can be swapped with a, b = b, a
.
Combine the two above::
>>> my_list[7:10], my_list[2:4] = my_list[2:4], my_list[7:10]
>>> my_list
[0, 1, 7, 8, 9, 4, 5, 6, 2, 3, 10, 11, 12]
Beware that - if slices have different sizes - the order is important: If you swap in the opposite order, you end up with a different result, because it will change first the initial items (lower indices), and then the higher index items (but those will be shifted in a different position by the first assignment).
Also, slices must not overlap.
Upvotes: 30
Reputation: 46759
Not exactly obvious (or efficient) but it works. I was curious to see if the slice object could be further utilised.
import itertools
def replace(s1, s2, l):
lslice = [slice(0,s1.start), s2, slice(s1.stop, s2.start), s1, slice(s2.stop,len(l))]
return list(itertools.chain.from_iterable([l[x] for x in lslice]))
Upvotes: 2
Reputation: 6589
This is an other way to do it:
import itertools
my_list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
my_list2 = []
my_list2.extend((my_list[0:2],my_list[7:10],my_list[4:7],my_list[2:4],my_list[10:]))
new_list = list(itertools.chain.from_iterable(my_list2)
new_list
print output:
[0, 1, 7, 8, 9, 4, 5, 6, 2, 3, 10, 11, 12]
Upvotes: 1
Reputation: 842
I think it's much better to use list index as parameters.
If you'd like to redefine your replace fuction like this:
my_list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
def slice_replace(src_list, l_start, l_end, r_start, r_end):
if l_end <= r_start:
return src_list[:l_start] + src_list[r_start:r_end] + src_list[l_end:r_start] + src_list[l_start:l_end] + src_list[r_end:]
else:
return slice_replace(src_list, r_start, r_end, l_start, l_end)
print my_list
new_list = slice_replace(my_list, 2, 4, 7, 10)
print new_list
new_list = slice_replace(my_list, 7, 10, 2, 4)
print new_list
I have fixed it.
Upvotes: 0
Reputation: 110069
You can use normal swapping technique (x,y = y,x
) here, but only if you perform the swap in the correct order: x
must be the second (rightmost) slice, while y
is the first (leftmost) slice.
>>> my_list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
>>> my_list[7:10], my_list[2:4] = my_list[2:4], my_list[7:10]
>>> my_list
[0, 1, 7, 8, 9, 4, 5, 6, 2, 3, 10, 11, 12]
This works because it will assign to my_list[7:10]
first, and only then to my_list[2:4]
.
If you perform this in the reverse order, assigning to my_list[2:4]
first will change the location of the items on the right due to the sublists having different lengths, which will give incorrect results.
Performance-wise, this may or may not be faster than your code: it probably depends on the length of the list and the slices. You will have to test it on typical use-cases to see.
Upvotes: 6
Reputation: 9969
I think it's best to use concatenation and slicing. If you pass the list and then two lists with index pairs then you can just slice apart the list and rearrange the two sublists. Note that indexA
and indexB
both work in the some way as regular slicing, the start number is included but the end one is not.
def replace(source, indexA, indexB):
newList = source[:indexA[0]] + source[indexB[0]:indexB[1]]
newList += source[indexA[1]:indexB[0]] + source[indexA[0]:indexA[1]]
newList += source[indexB[1]:]
return newList
myList = replace(myList, [2,4], [7,10])
Upvotes: 2