I am merging to list with ascending order - python

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))

Related

Find the longest increasing subarray

For example : input = [1,2,3,1,5,7,8,9] , output = [1,5,7,8,9]
find out the longest continuous increasing subarray
I have tried on my own like this :
def longsub(l):
newl = []
for i in range(len(l)) :
if l[i] < l[i+1] :
newl.append(l[i])
else :
newl = []
return newl
But it would get error since the list index out of range. (It could not get the value after last value)
def longsub(l):
newl = []
for i in range(len(l)) :
if l[i] > l[i-1] :
newl.append(l[i])
else :
newl = []
return newl
And then I did this, but I would get the result without the first value of increasing subarray.
What should I rectify my code? Thanks!
Suppose that you had this helper at your disposal:
def increasing_length_at(l, i):
"""Returns number of increasing values found at index i.
>>> increasing_length_at([7, 6], 0)
1
>>> increasing_length_at([3, 7, 6], 0)
2
"""
val = l[i] - 1
for j in range(i, len(l)):
if l[j] <= val: # if non-increasing
break
val = l[j]
return j - i
How could you use that as part of a solution?
You could use 2 loops (first to iterate over the input and the second loop to iterate from the index of the first loop until the end):
inp = [1,2,3,1,5,7,8,9]
output = [1,5,7,8,9]
i, res = 0, []
while i < len(inp):
tempResult = [startNum := inp[i]] # Python>3.8: Walrus operator
for j in range(i+1, len(inp)):
if startNum > inp[j]:
i = j-1 # skip already compared items!
break
tempResult.append(startNum := inp[j]) # Python>3.8: Walrus operator
if len(tempResult) > len(res):
res = tempResult
i += 1
print(res, res == output)
Out:
[1, 5, 7, 8, 9] True
Firstly, you can use len(l) - 1 to avoid the IndexError. However, your approach is invalid since this would just return the last increasing sub. Here's my approach:
def longsub(l):
res, newl = [], []
for i in range(len(l)-1):
if l[i] < l[i+1]:
newl.append(l[i])
else:
newl.append(l[i])
res.append(newl)
newl = []
if newl: res.append(newl)
return max(res, key=len)
input = [1,2,3,4,5,1,5,7,8,9]
print(longsub(input))
Output:
>>> [1, 2, 3, 4, 5]

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]

How do I loop through two lists only iterating one at a time?

Im trying to implement merge sort in python and I'd like to know what is the pythonic way of looping through two lists iterating only one of the lists each loop.
This is what I have now (using indexes)
def merge(array1, array2):
final = []
i = 0
j = 0
while i < len(array1) or j < len(array2):
if array1[i] <= array2[j]:
final.append(array1[i])
i += 1
elif array2[j] < array1[i]:
final.append(array2[j])
j += 1
# Finished one of the arrays
if i == len(array1):
final.extend(array2[j:])
break
elif j == len(array2):
final.extend(array1[i:])
break
return final
Thanks.
I'm not sure not sure your problem here is using indices, but there's a lot of unneeded code.
Firstly, we can change your initialisers to:
final, i, j = [], 0, 0
Next, if we change your while condition we can remove the breaks:
while i < len(array1) and j < len(array2):
Your elif isn't meaningful as it will always be true, we can thus make your if:
if array1[i] <= array2[j]:
final.append(array1[i])
i += 1
else:
final.append(array2[j])
j += 1
And now because we're automatically breaking out of the loop we don't need the breaks and can move the extends outside:
def merge(array1, array2):
final, i, j = [], 0, 0
while i < len(array1) and j < len(array2):
if array1[i] <= array2[j]:
final.append(array1[i])
i += 1
else:
final.append(array2[j])
j += 1
final.extend(array1[i:])
final.extend(array2[j:])
return final
This gives you smaller, more readable code without actually changing the way you're doing anything, that way you'll still understand it.
Note that we can perform:
final.extend(array1[i:])
final.extend(array2[j:])
outside the loop because one array will have contents, for example [7,9], while the other will be empty([]):
>>> final = [3,6]
>>> array1 = [3,6]
>>> array2 = [7,9]
>>> i = 2
>>> j = 0
>>> array1[i:]
[]
>>> array2[j:]
[7, 9]
>>> final.extend(array1[i:])
>>> final
[3, 6]
>>> final.extend(array2[j:])
>>> final
[3, 6, 7, 9]

Swapping list with index

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

Categories