Swapping list with index - python

Just want to ask how do i swap the list at the index with the list that follows it and if the list at the index is on the bottom, swap that with the top.
So that the index would swap places with the position the list is with the next number For example Normal = [1,2,3,4] and index of 1 would turn to = [1, 3, 2, 4]. making the 2 and 3 swap places and index of 3 would make [4, 2, 3, 1]

def swap(lst, swap_index):
try:
next_index = (swap_index + 1) % len(lst)
lst[swap_index], lst[next_index] = lst[next_index], lst[swap_index]
except IndexError:
print "index out of range"
lst = [1,2,3,4]
swap_index = 4
swap(lst,swap_index)
print lst
pay attention that everything in Python is reference, that is to say, the swap function swap elements in place

I threw together a quick function which should work with any values, though Hootings way may be better.
def itatchi_swap(x, n):
x_len = len(x)
if not 0 <= n < x_len:
return x
elif n == x_len - 1:
return [x[-1]] + x[1:-1] + [x[0]]
else:
return x[:n] + [x[n+1]] + [x[n]] + x[n+2:]
And slightly modified to mutate the list:
def itatchi_swap(x, n):
x_len = len(x)
if 0 <= n < x_len:
if n == x_len - 1:
v = x[0]
x[0] = x[-1]
x[-1] = v
else:
v = x[n]
x[n] = x[n+1]
x[n+1] = v

Related

How do I find the index of variable b from list a?

How do I find the index of variable b from list a?
I suspect that the problem is in the data types.
a=[-202516736, '-202516736', '13886', '678280946', '14514', '330251838', '14511', '639566631', '14510', '542472303', '14506']
b=['678280946']
a_INT = [int(item) for item in a]
b_INT = [int(item) for item in b]
j = 0
while True:
try:
i = a_INT.index(b_INT, j)
print(i)
j = i + 1
except:
break
Let's take this a step further and add another value to the b list and also add a duplicate in the a list. Then:
a=[-202516736, '-202516736', '13886', '678280946', '14514', '678280946', '330251838', '14511', '639566631', '14510', '542472303', '14506']
b=['678280946', 13886]
ai = list(map(int, a))
for n in map(int, b):
offset = 0
r = []
while True:
try:
i = ai[offset:].index(n)
r.append(offset+i)
offset += i + 1
except ValueError:
break
print(f'{n} occurs at {r}')
Output:
678280946 occurs at [3, 5]
13886 occurs at [2]
Version 2:
The first piece of code is functionally correct. However, it could be very inefficient if the list being searched is very large.
Python's built-in sort function is very fast. So, let's build a list of 2-tuples each made up of a value from the list and its original index. Then sort the new list. Now that it's sorted we can perform a binary search and move on from there.
Added some more values to the OP's original list for demonstration purposes:
a = [-202516736, '-202516736', '13886', '678280946', '14514', '678280946',
'330251838', '14511', '639566631', '14510', '542472303', '14506', '678280946']
b = ['678280946', 13886, 14514, '-202516736', 99]
def bsearch(lst, x):
L = 0
R = len(lst) - 1
while L <= R:
m = (L + R) // 2
if (v := lst[m][0]) == x:
return m
if v < x:
L = m + 1
else:
R = m - 1
return -1
def findall(list_, n):
templist = sorted((v, i) for i, v in enumerate(list_))
result = None
if (i := bsearch(templist, n)) >= 0:
result = [templist[i][1]]
for j in range(i-1, -1, -1):
if templist[j][0] != n:
break
result.append(templist[j][1])
for j in range(i+1, len(templist)):
if templist[j][0] != n:
break
result.append(templist[j][1])
return result
ai = list(map(int, a))
for n in map(int, b):
print(f'{n} -> {findall(ai, n)}')
Output:
678280946 -> [5, 3, 12]
13886 -> [2]
14514 -> [4]
-202516736 -> [0, 1]
99 -> None
a=[-202516736, '-202516736', '13886', '678280946', '14514', '330251838', '14511', '639566631', '14510', '542472303', '14506']
b=['678280946']
for item in b:
print(a.index(item))
Since b has only one element the output is 3.

I am merging to list with ascending order

If either i or j reach the end of their list range, how i copy the remainder of the other
list to the merge list
https://ibb.co/m9JzBYp
go to link if not get the question
def list_merge (x, y):
merge = []
i = 0
j = 0
total = len (x) + len(y)
while != total :
if x[i] < y[j]:
merge.append(x[i])
i += 1
if i >= len (x):
#how i copy the reminder
else :
merge.append(y[j])
j += 1
if j >= len (y):
#how i copy the reminder
return merge
EDIT - OP wanted the code in some specific way.. Please see second snippet.
Don't try to complicate your code.. just go with how you would do it manually and write the code.
def list_merge (x, y):
merged_list = []
i = 0
j = 0
# when one of them is empty, break out
while i < len(x) and j < len(y):
if x[i] <= y[j]:
merged_list.append(x[i])
i +=1
else:
merged_list.append(y[j])
j +=1
# if you are here, that means either one of the list is done
# so check the bounds of both lists and append the one which is not traversed
# x has some elements to add
# check how extend works... makes your code clean
if i != len(x):
merged_list.extend(x[i:])
else:
merged_list.extend(y[j:])
return merged_list
a = [1,3,5,7,10]
b = [2,4,6,8,100]
print(list_merge(a,b))
Output
[1, 2, 3, 4, 5, 6, 7, 8, 10, 100]
What OP needed
def list_merge (x, y):
merge = []
i = 0
j = 0
total = len (x) + len(y)
while len(merge) != total :
if x[i] < y[j]:
merge.append(x[i])
i += 1
if i >= len (x):
merge.extend(y[j:])
else:
merge.append(y[j])
j += 1
if j >= len (y):
merge.extend(x[i:])
return merge
a quite simple form:
def merge(*lists_in):
list_in = []
for l in lists_in:
list_in += l
i = 0
while True:
if i == list_in.__len__() -1:
break
if list_in[i] > list_in[i+1]:
temp = list_in[i]
list_in[i] = list_in[i+1]
list_in[i+1] = temp
i = 0
else:
i += 1
return list_in
Testing it:
list1 = [1,4]
list2 = [1,5,6]
list3 = [3,7,9,10]
print(merge(list1, list2, list3))
Out:
[1, 1, 3, 4, 5, 6, 7, 9, 10]
This can be solved rather nicely using deques, which allow you to efficiently look at and remove the first element.
from collections import deque
# A generator function that interleaves two sorted deques
# into a single sorted sequence.
def merge_deques(x, y):
while x and y:
yield (x if x[0] <= y[0] else y).popleft()
# When we reach this point, one of x or y is empty,
# so one of these doesn't yield any values.
yield from x
yield from y
# Makes a list by consuming the merged sequence of two deques.
def list_merge(x, y):
return list(merge_deques(deque(x), deque(y))

Finding first pair of numbers in array that sum to value

Im trying to solve the following Codewars problem: https://www.codewars.com/kata/sum-of-pairs/train/python
Here is my current implementation in Python:
def sum_pairs(ints, s):
right = float("inf")
n = len(ints)
m = {}
dup = {}
for i, x in enumerate(ints):
if x not in m.keys():
m[x] = i # Track first index of x using hash map.
elif x in m.keys() and x not in dup.keys():
dup[x] = i
for x in m.keys():
if s - x in m.keys():
if x == s-x and x in dup.keys():
j = m[x]
k = dup[x]
else:
j = m[x]
k = m[s-x]
comp = max(j,k)
if comp < right and j!= k:
right = comp
if right > n:
return None
return [s - ints[right],ints[right]]
The code seems to produce correct results, however the input can consist of array with up to 10 000 000 elements, so the execution times out for large inputs. I need help with optimizing/modifying the code so that it can handle sufficiently large arrays.
Your code inefficient for large list test cases so it gives timeout error. Instead you can do:
def sum_pairs(lst, s):
seen = set()
for item in lst:
if s - item in seen:
return [s - item, item]
seen.add(item)
We put the values in seen until we find a value that produces the specified sum with one of the seen values.
For more information go: Referance link
Maybe this code:
def sum_pairs(lst, s):
c = 0
while c<len(lst)-1:
if c != len(lst)-1:
x= lst[c]
spam = c+1
while spam < len(lst):
nxt= lst[spam]
if nxt + x== s:
return [x, nxt]
spam += 1
else:
return None
c +=1
lst = [5, 6, 5, 8]
s = 14
print(sum_pairs(lst, s))
Output:
[6, 8]
This answer unfortunately still times out, even though it's supposed to run in O(n^3) (since it is dominated by the sort, the rest of the algorithm running in O(n)). I'm not sure how you can obtain better than this complexity, but I thought I might put this idea out there.
def sum_pairs(ints, s):
ints_with_idx = enumerate(ints)
# Sort the array of ints
ints_with_idx = sorted(ints_with_idx, key = lambda (idx, num) : num)
diff = 1000000
l = 0
r = len(ints) - 1
# Indexes of the sum operands in sorted array
lSum = 0
rSum = 0
while l < r:
# Compute the absolute difference between the current sum and the desired sum
sum = ints_with_idx[l][1] + ints_with_idx[r][1]
absDiff = abs(sum - s)
if absDiff < diff:
# Update the best difference
lSum = l
rSum = r
diff = absDiff
elif sum > s:
# Decrease the large value
r -= 1
else:
# Test to see if the indexes are better (more to the left) for the same difference
if absDiff == diff:
rightmostIdx = max(ints_with_idx[l][0], ints_with_idx[r][0])
if rightmostIdx < max(ints_with_idx[lSum][0], ints_with_idx[rSum][0]):
lSum = l
rSum = r
# Increase the small value
l += 1
# Retrieve indexes of sum operands
aSumIdx = ints_with_idx[lSum][0]
bSumIdx = ints_with_idx[rSum][0]
# Retrieve values of operands for sum in correct order
aSum = ints[min(aSumIdx, bSumIdx)]
bSum = ints[max(aSumIdx, bSumIdx)]
if aSum + bSum == s:
return [aSum, bSum]
else:
return None

shorten a list of integers by sums of contiguous positive or negative numbers

I would like to write a function to process a list of integers, best way is to show as an example:
input [0,1,2,3, -1,-2,-3, 0,1,2,3, -1,-2,-3] will return [6,-6,6,-6]
I have a draft here that will actually work:
def group_pos_neg_list(nums):
p_nums = []
# to determine if the first element >=0 or <0
# create pos_combined and neg_combined as a list to check the length in the future
if nums[0] >= 0:
pos_combined, neg_combined = [nums[0]], []
elif nums[0] < 0:
pos_combined, neg_combined = [], [nums[0]]
# loop over each element from position 1 to the end
# accumulate pos num and neg nums and set back to 0 if next element is different
index = 1
while index < len(nums):
if nums[index] >= 0 and nums[index-1] >= 0: # both posivite
pos_combined.append(nums[index])
index += 1
elif nums[index] < 0 and nums[index-1] < 0: # both negative
neg_combined.append(nums[index])
index += 1
else:
if len(pos_combined) > 0:
p_nums.append(sum(pos_combined))
pos_combined, neg_combined = [], [nums[index]]
elif len(neg_combined) > 0:
p_nums.append(sum(neg_combined))
pos_combined, neg_combined = [nums[index]], []
index += 1
# finish the last combined group
if len(pos_combined) > 0:
p_nums.append(sum(pos_combined))
elif len(neg_combined) > 0:
p_nums.append(sum(neg_combined))
return p_nums
But I am not quite happy with it, because it looks a bit complicate.
Especially that there is a repeating part of code:
if len(pos_combined) > 0:
p_nums.append(sum(pos_combined))
pos_combined, neg_combined = [], [nums[index]]
elif len(neg_combined) > 0:
p_nums.append(sum(neg_combined))
pos_combined, neg_combined = [nums[index]], []
I have to write this twice as the final group of integers will not be counted in the loop, so an extra step is needed.
Is there anyway to simplify this?
Using groupby
No need to make it that complex: we can first groupby the signum, and then we can calculate the sum, so:
from itertools import groupby
[sum(g) for _, g in groupby(data, lambda x: x >= 0)]
This then produces:
>>> from itertools import groupby
>>> data = [0,1,2,3, -1,-2,-3, 0,1,2,3, -1,-2,-3]
>>> [sum(g) for _, g in groupby(data, lambda x: x >= 0)]
[6, -6, 6, -6]
So groupby produces tuples with the "key" (the part we calculate with the lambda), and an iterable of the "burst" (a continuous subsequence of elements with the same key). We are only interested in the latter g, and then calculate sum(g) and add that to the list.
Custom made algorithm
We can also write our own version, by using:
swap_idx = [0]
swap_idx += [i+1 for i, (v1, v2) in enumerate(zip(data, data[1:]))
if (v1 >= 0) != (v2 >= 0)]
swap_idx.append(None)
our_sums = [sum(data[i:j]) for i, j in zip(swap_idx, swap_idx[1:])]
Here we first construct a list swap_idx that stores the indices where of the element where the signum changes. So for your sample code that is:
>>> swap_idx
[0, 4, 7, 11, None]
The 0 and None are added by the code explicitly. So now that we identified the points where the sign has changed, we can sum these subsequences together, with sum(data[i:j]). We thus use zip(swap_idx, swap_idx[1:]) to obtain two consecutive indices, and thus we can then sum that slice together.
More verbose version
The above is not very readable: yes it works, but it requires some reasoning. We can also produce a more verbose version, and make it even more generic, for example:
def groupby_aggregate(iterable, key=lambda x: x, aggregate=list):
itr = iter(iterable)
nx = next(itr)
kx = kxcur = key(nx)
current = [nx]
try:
while True:
nx = next(itr)
kx = key(nx)
if kx != kxcur:
yield aggregate(current)
current = [nx]
kxcur = kx
else:
current.append(nx)
except StopIteration:
yield aggregate(current)
We can then use it like:
list(groupby_aggregate(data, lambda x: x >= 0, sum))
You can use itertools.groupby, utilizing a key to group by all the values greater than or equal to zero:
import itertools
s = [0,1,2,3, -1,-2,-3, 0,1,2,3, -1,-2,-3]
new_s = [sum(b) for a, b in itertools.groupby(s, key=lambda x: x >=0)]
Output:
[6, -6, 6, -6]
Here is a way to do without any external imports, only using reduce():
def same_sign(a, b):
"""Returns True if a and b have the same sign"""
return (a*b>0) or (a>=0 and b>=0)
l = [0,1,2,3, -1,-2,-3, 0,1,2,3, -1,-2,-3]
reduce(
lambda x, y: (x+y if same_sign(x,y) else [x, y]) if not isinstance(x, list) else x[:-1] + [x[-1] + y] if same_sign(x[-1],y) else x + [y],
l
)
#[6, -6, 6, -6]
Explanation
This is a bit hard to explain, but I'll try.
From the docs calling reduce() will:
Apply function of two arguments cumulatively to the items of iterable, from left to right
In this case I take two values (x and y) from your list and do the following:
If x is not a list:
If x and y have the same sign (product >=0), sum them
Otherwise return a list [x, y]
If x is a list, only modify the last element of x.
If the signs match, add y.
Otherwise append a new element to the list x
Note
You probably shouldn't do it this way because the code is hard to read and understand. I just wanted to show that it was possible.
Update
A more readable version of the same code above:
def same_sign(a, b):
"""Returns True if a and b have the same sign"""
return (a*b>0) or (a>=0 and b>=0)
l = [0,1,2,3, -1,-2,-3, 0,1,2,3, -1,-2,-3]
def reducer(x, y):
if isinstance(x, list):
if same_sign(x[-1], y):
return x[:-1] + [x[-1] + y]
else:
return x + [y]
else:
if same_sign(x, y):
return x+y
else:
return [x, y]
reduce(reducer, l)
#[6, -6, 6, -6]

Find/extract a sequence of integers within a list in python

I want to find a sequence of n consecutive integers within a sorted list and return that sequence. This is the best I can figure out (for n = 4), and it doesn't allow the user to specify an n.
my_list = [2,3,4,5,7,9]
for i in range(len(my_list)):
if my_list[i+1] == my_list[i]+1 and my_list[i+2] == my_list[i]+2 and my_list[i+3] == my_list[i]+3:
my_sequence = list(range(my_list[i],my_list[i]+4))
my_sequence = [2,3,4,5]
I just realized this code doesn't work and returns an "index out of range" error, so I'll have to mess with the range of the for loop.
Here's a straight-forward solution. It's not as efficient as it might be, but it will be fine unless you have very long lists:
myarray = [2,5,1,7,3,8,1,2,3,4,5,7,4,9,1,2,3,5]
for idx, a in enumerate(myarray):
if myarray[idx:idx+4] == [a,a+1,a+2,a+3]:
print([a, a+1,a+2,a+3])
break
Create a nested master result list, then go through my_sorted_list and add each item to either the last list in the master (if discontinuous) or to a new list in the master (if continuous):
>>> my_sorted_list = [0,2,5,7,8,9]
>>> my_sequences = []
>>> for idx,item in enumerate(my_sorted_list):
... if not idx or item-1 != my_sequences[-1][-1]:
... my_sequences.append([item])
... else:
... my_sequences[-1].append(item)
...
>>> max(my_sequences, key=len)
[7, 8, 9]
A short and concise way is to fill an array with numbers every time you find the next integer is the current integer plus 1 (until you already have N consecutive numbers in array), and for anything else, we can empty the array:
arr = [4,3,1,2,3,4,5,7,5,3,2,4]
N = 4
newarr = []
for i in range(len(arr)-1):
if(arr[i]+1 == arr[i+1]):
newarr += [arr[i]]
if(len(newarr) == N):
break
else:
newarr = []
When the code is run, newarr will be:
[1, 2, 3, 4]
#size = length of sequence
#span = the span of neighbour integers
#the time complexity is O(n)
def extractSeq(lst,size,span=1):
lst_size = len(lst)
if lst_size < size:
return []
for i in range(lst_size - size + 1):
for j in range(size - 1):
if lst[i + j] + span == lst[i + j + 1]:
continue
else:
i += j
break
else:
return lst[i:i+size]
return []
mylist = [2,3,4,5,7,9]
for j in range(len(mylist)):
m=mylist[j]
idx=j
c=j
for i in range(j,len(mylist)):
if mylist[i]<m:
m=mylist[i]
idx=c
c+=1
tmp=mylist[j]
mylist[j]=m
mylist[idx]=tmp
print(mylist)

Categories