reverse ascending sequences in a list - python

Trying to figure out how to reverse multiple ascending sequences in a list.
For instance: input = [1,2,2,3] to output = [2,1,3,2].
I have used mylist.reverse() but of course it reverses to [3,2,2,1]. Not sure which approach to take?
Example in detail:
So lets say [5, 7, 10, 2, 7, 8, 1, 3] is the input - the output should be [10,7,5,8,7,2,3,1]. In this example the first 3 elements 5,7,10 are in ascending order, 2,7,8 is likewise in ascending order and 1,3 also in ascending order. The function should be able to recognize this pattern and reverse each sequence and return a new list.

All you need is to find all non-descreasing subsequences and reverse them:
In [47]: l = [5, 7, 10, 2, 7, 8, 1, 3]
In [48]: res = []
In [49]: start_idx = 0
In [50]: for idx in range(max(len(l) - 1, 0)):
...: if l[idx] >= l[idx - 1]:
...: continue
...: step = l[start_idx:idx]
...: step.reverse()
...: res.extend(step)
...: start_idx = idx
...:
In [51]: step = l[start_idx:]
In [52]: step.reverse()
In [53]: res.extend(step)
In [54]: print(res)
[10, 7, 5, 8, 7, 2, 3, 1]
For increasing subsequences you need to change if l[idx] >= l[idx - 1] to if l[idx] > l[idx - 1]

Walk the list making a bigger and bigger window from x to y positions. When you find a place where the next number is not ascending, or reach the end, reverse-slice the window you just covered and add it to the end of an output list:
data = [5, 7, 10, 2, 7, 8, 1, 3]
output = []
x = None
for y in range(len(data)):
if y == len(data) - 1 or data[y] >= data[y+1]:
output.extend(data[y:x:-1])
x = y
print(output)

There is probably a more elegant way to do this, but one approach would be to use itertools.zip_longest along with enumerate to iterate over sequential element pairs in your list and keep track of each index where the sequence is no longer ascending or the list is exhausted in order to slice, reverse, and extend your output list with the sliced items.
from itertools import zip_longest
d = [5, 7, 10, 2, 7, 8, 1, 3]
results = []
stop = None
for i, (a, b) in enumerate(zip_longest(d, d[1:])):
if not b or b <= a:
results.extend(d[i:stop:-1])
stop = i
print(results)
# [10, 7, 5, 8, 7, 2, 3, 1]

data = [5, 7, 10, 2, 7, 8, 1, 3,2]
def func(data):
result =[]
temp =[]
data.append(data[-1])
for i in range(1,len(data)):
if data[i]>=data[i-1]:
temp.append(data[i-1])
else:
temp.append(data[i-1])
temp.reverse()
result.extend(temp)
temp=[]
if len(temp)!=0:
temp.reverse()
result.extend(temp)
temp.clear()
return result
print(func(data))
# output [10, 7, 5, 8, 7, 2, 3, 1, 2]

You could define a general handy method which returns slices of an array based on condition (predicate).
def slice_when(predicate, iterable):
i, x, size = 0, 0, len(iterable)
while i < size-1:
if predicate(iterable[i], iterable[i+1]):
yield iterable[x:i+1]
x = i + 1
i += 1
yield iterable[x:size]
Now, the slice has to be made when the next element is smaller then the previous, for example:
array = [5, 7, 10, 2, 7, 8, 1, 3]
slices = slice_when(lambda x,y: x > y, array)
print(list(slices))
#=> [[5, 7, 10], [2, 7, 8], [1, 3]]
So you can use it as simple as:
res = []
for e in slice_when(lambda x,y: x > y, array):
res.extend(e[::-1] )
res #=> [10, 7, 5, 8, 7, 2, 3, 1]

Related

Why the 'list' is resulting empty after getting appended with a deleted list?

Here is my code, Could anyone please help me why the 'long_band' resulted in empty list?
Expected output: [[9,10],[0,1,2,3,4,5,6,7],[18],[12]]
Code begins here:
arr = [1, 9, 3, 0, 18, 5, 2, 4, 10, 7, 12, 6]
len1 = len(arr)
long_band = list()
chain = list()
def band(i):
chain.append(i)
j = i+1
if j in arr:
band(j)
else:
#print(chain)
#print(long_band)
long_band.append([chain])
#print(long_band)
del chain[:]
def isHead(n):
x = n-1
if x in arr:
None
else:
band(n)
for i in range(len1):
isHead(arr[i])
print(long_band)
The immediate problem is this:
long_band.append([chain])
del chain[:]
You're appending the chain object to the list, and then immediately clear it. This also clears the object in the list, because there's only one object. Appending it to a list doesn't create a copy of it.
The bigger problem is that your algorithm is way overcomplicated. Here's a much simpler approach:
>>> arr = [1, 9, 3, 0, 18, 5, 2, 4, 10, 7, 12, 6]
>>> arr.sort()
>>> res = [arr[0:1]]
>>> for i, j in zip(arr, arr[1:]):
... if i != j - 1:
... res.append([])
... res[-1].append(j)
...
>>> res
[[0, 1, 2, 3, 4, 5, 6, 7], [9, 10], [12], [18]]
arr = [1, 9, 3, 0, 18, 5, 2, 4, 10, 7, 12, 6]
len1 = len(arr)
long_band = list()
chain = list()
def band(i):
chain.append(i)
j = i+1
if j in arr:
band(j)
else:
long_band.append(chain[:])
del chain[:]
def isHead(n):
x = n-1
if x in arr:
None
else:
band(n)
def FindMaxLen(long_band):
maxLength = max(len(x) for x in long_band)
print(maxLength)
for i in range(len1):
isHead(arr[i])
print(long_band)
FindMaxLen(long_band)

Remove integer on a list if it is smaller than the previous integer on the list

I have an unordered list of numbers
num_list=[3 4 5 1 2 5 6 3 1 2 6 9]
I want to run through the list and remove all numbers that are smaller than the previous such that the list is ordered in ascending order after deleting such numbers. How can I do that?
Expected output:
num_list=[3 4 5 5 6 6 9]
One simple approach would be to iteratively add values from num_list to a new list if they satisfy the condition of being greater than the last appended value:
out = [num_list[0]]
for i in num_list[1:]:
if i >= out[-1]:
out.append(i)
print(out)
# [3, 4, 5, 5, 6, 6, 9]
Short approach with functools.reduce function:
import functools
num_list = [3,4,5,1,2,5,6,3,1,2,6,9]
res = functools.reduce(lambda x, y: x if y < x[-1] else x + [y],
num_list[1:], [num_list[0]])
print(res)
The output:
[3, 4, 5, 5, 6, 6, 9]
A simple approach using list.pop
num_list = [3, 4, 5, 1, 2, 5, 6, 3, 1, 2, 6, 9]
i = 1
while (i < len(num_list)):
if num_list[i] < num_list[i - 1]:
num_list.pop(i)
else:
i += 1
print(num_list)
# [3, 4, 5, 5, 6, 6, 9]
A comprehension could be used for this:
num_list = [3, 4, 5, 1, 2, 5, 6, 3, 1, 2, 6, 9]
(x for i, x in enumerate(num_list) if all(x >= j for j in num_list[:i]))
Though it's not as efficient as yatu's answer.
Version with itertools.groupby:
from itertools import groupby
num_list=[3, 4, 5, 1, 2, 5, 6, 3, 1, 2, 6, 9]
out = [num_list[0]]
[out.extend(g) for v, g in groupby(num_list[1:], lambda k: k>=out[-1]) if v]
print(out)
Prints:
[3, 4, 5, 5, 6, 6, 9]
Here is another one-liner solution using itertools.accumulate.
from itertools import accumulate
result = [n for n, cur_max in zip(l, accumulate(l, max)) if n >= cur_max]
Despite being concise, it is actually much less efficient than the following solution using an explicit for loop. This is also the most efficient solution as of now.
cur_max = l[0]
result = []
for e in l:
if e >= cur_max:
cur_max = e
result.append(e)

Reversing order in incrementing digits

I have a list of numbers, and I'm trying to do the following in a way as efficient as possible.
For each consecutively incrementing chunk in the list I have to reverse its order.
This is my attempt so far:
l = []
l_ = []
i = 0
while i <= len(a)-1:
if a[i] < a[i+1]:
l_= l_ + [a[i]]
else:
l = l_ + [a[i]]
l_ = []
i = i + 1
I'd appreciate any guidance or other approaches.
So, for the following list:
a = [1,5,7,3,2,5,4,45,1,5,10,12]
I would like to obtain:
[7,5,1,3,5,2,45,4,12,10,5,1]
Try this:
(with fixes from #Scott Boston and #myrmica)
nums = [1, 3, 5, 4, 6, 8, 9, 7, 2, 4] # sample input
chunk = [] # keep track of chunks
output = [] # output list
for i in nums:
if chunk and i < chunk[-1]:
output.extend(chunk[::-1]) # add reversed chunk to output
chunk[:] = [i] # clear chunk
else:
chunk.append(i) # add to chunk
output.extend(chunk[::-1]) # empty leftover chunk
print(output)
with comprehension lists :
a = [1,5,7,3,2,5,4,45,1,5,10,12]
split=[0]+[i for i in range(1,len(a)) if a[i-1]>a[i]]+[len(a)]
#[0, 3, 4, 6, 8, 12]
chunks=[list(reversed(a[i:j])) for i,j in zip(split[:-1],split[1:])]
#[[7, 5, 1], [3], [5, 2], [45, 4], [12, 10, 5, 1]]
print(sum(chunks,[]))
#[7, 5, 1, 3, 5, 2, 45, 4, 12, 10, 5, 1]

I want to remove duplicate from list (without for loop)

Here I have a list
a = [1, 2, 1, 4, 5, 7, 8, 4, 6]
Now I want a following output but without for loop.
Remove all the duplicate from the list.
[2, 5, 7, 8, 6]
output list contain only single occurrence number
Given: a = [1, 2, 1, 4, 5, 7, 8, 4, 6]
One liner:
b = [x for x in a if a.count(x) == 1]
You can use a Counter and a conditional list comprehension or filter in order to maintain the original order:
from collections import Counter
c = Counter(a)
clean_a = filter(lambda x: c[x] == 1, a) # avoids 'for' ;-)
# clean_a = list(filter(lambda x: c[x] == 1, a)) # Python3, if you need a list
# clean_a = [x for x in a if c[a] == 1] # would be my choice
This is a very simple and inefficient implementation.
We use a while loop to access every element of a. In the loop we check if the current element appears only once in the list. If yes, we add it to a new list.
a = [1, 2, 1, 4, 5, 7, 8, 4, 6]
index = 0
result = []
while index < len(a):
if a.count(a[index]) == 1:
result.append(a[index])
index += 1
print(result)
def cleaner(LIST, pos):
if len(LIST)>pos:
if LIST[pos] in LIST[pos+1:]:
LIST.pop(pos)
# OR
# LIST.remove(LIST[pos])
cleaner(LIST, pos)
else:
pos+=1
cleaner(LIST, pos)
return LIST
LIST = [1, 2, 1, 4, 5, 7, 8, 4, 6]
print(cleaner(LIST, 0))

function to sort a list of positive numbers to return odd numbers first and even numbers last

i want to write a function that takes in a list of numbers (positive integers) and returns a list of sorted numbers such that odd numbers come first and even numbers come last
For example:
my_sort([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) => [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
my_sort([1, 2]) => [1, 2]
my_sort([2, 1]) => [1, 2]
my_sort([3, 3, 4]) => [3, 3, 4]
my_sort([90, 45, 66]) => [45, 66, 90]'''
This is my code
def my_sort(numbers):
a = [n for n in numbers if n % 2 != 0]
b = [n for n in numbers if n % 2 == 0]
new_num = b + a
for m in numbers:
if a and b:
return new_num
else:
return "Invalid sorted output"
Which fails all the test. I'm new to programming and python. So I'ld appreciate if anyone could help me with this.
And here is the unittest
import unittest
class MySortTestCases(unittest.TestCase):
def setUp(self):
self.result1 = my_sort([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
self.result2 = my_sort([1, 2])
self.result3 = my_sort([2, 1])
self.result4 = my_sort([3, 3, 4])
self.result5 = my_sort([90, 45, 66])
def test_output_1(self):
self.assertEqual(self.result1, [1, 3, 5, 7, 9, 2, 4, 6, 8, 10],
msg='Invalid sorted output')
def test_output_2(self):
self.assertEqual(self.result2, [1, 2], msg='Invalid sorted
output')
def test_output_3(self):
self.assertEqual(self.result3, [1, 2], msg='Invalid sorted
output')
def test_output_4(self):
self.assertEqual(self.result4, [3, 3, 4], msg='Invalid sorted
output')
def test_output_5(self):
self.assertEqual(self.result5, [45, 66, 90], msg='Invalid
sorted output')
You could so it by splitting the list into odd and even and then sorting both and concatenating the two lists.
def my_sort(numbers):
odd = [n for n in numbers if n % 2 != 0]
even = [n for n in numbers if n % 2 == 0]
return sorted(odd) + sorted(even)
See that this
>>> my_sort([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
[1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
But using a key function avoids constructing the split lists:
>>> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> numbers.sort(key=lambda v: (v%2==0, v))
>>> numbers
[1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
This sorts the list using a key function that returns a tuple of (0, v) if v is odd and (1, v) if even. This causes the odd numbers to appear before the even numbers in an ascending ordered sort.
It can be turned into a function:
def my_sort(numbers):
return sorted(numbers, key=lambda v: (v%2==0, v))
So, I went ahead and grabbed your code and did just a few tests by myself to see the actual output. Your code doesn't actually sort the list currently. It only puts the even numbers first and the odd numbers second. Which by the sound of it isn't what you want either.
>>> my_sort([4, 5, 7, 1, 2, 6, 3])
[4, 2, 6, 5, 7, 1, 3]
>>> my_sort([1, 2])
[2, 1]
These were the outputs I got by running your code using the Python interpreter. As you can see the even numbers are first, unsorted, followed by the odd numbers, also unsorted. This just has to do with the way you made the new_num list. You have new_num = b + a, but you created b by looking for all the even numbers b = [n for n in numbers if n % 2 == 0] and created a by looking for all the odd numbers a = [n for n in numbers if n % 2 != 0]. The % returns the remainder. So, if a number is divisible by 2 it returns 0 and this means that it is even. So, you can either flip the assignment of a and b or you can flip when you add them together so a is first and b is second.
As for the individual chunks not being sorted. Python has a built in sorted function that you can call on list sorted(my_list) that returns a sorted version of that list. So, if you just run that on your a and b when you're adding them together to create your new_num list then the numbers for each should be sorted just fine.
Your if statements at the end inside your for loop are also not working properly. Giving an already sorted list just returns the list given. Your code here:
for m in numbers:
if a and b:
return new_num
else:
return "Invalid sorted output"
This is looping through the original list given and returns the new_num if a and b exists. Since a and b will always exist this will always return new_num and never the "Invalid sorted output" statement. You need to make sure you're checking to see if the numbers list is already sorted with the expected output of your function. My suggestion would be check to see if new_num is the same as numbers.
Just use sorted twice. First to sort the list, and again to sort it by odd then even using the key parameter.
x = [3,5,4,1,6,8,10,2,9,7]
sorted(sorted(x), key=lambda x: (x+1)%2)
# returns:
# [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
This should work... It makes sense if you are joining Andela.
def my_sort(theNumbers):
#pass
evenNumbers=[]
oddNumbers=[]
#CHECK IF EVEN OR ODD THEN STORE THEN SEPARATE
for i in theNumbers:
if i%2==0:
evenNumbers.append(i)
else:
oddNumbers.append(i)
#print (evenNumbers)
#SORT DEM LISTS
evenNumbers.sort()
oddNumbers.sort()
#print (evenNumbers)
#join the two
oddNumbers+=evenNumbers
print (oddNumbers)
#my_sort([11111, 1, 11, 979749, 1111, 1111])
This code will sort even and odd numbers without creating a temporary list.
def segregateEvenOdd(arr, index=0, iterations=0):
if iterations == len(arr):
return arr
if arr[index]%2 == 0:
arr.append(arr[index])
arr.pop(index)
return segregateEvenOdd(arr, index, iterations+1 )
if arr[index]%2 != 0:
return segregateEvenOdd(arr,index+1,iterations+1)
arr = [ 2, 3, 9, 45, 2, 5, 10, 47 ]
output = segregateEvenOdd(arr)
print(output)
# Output
# [3, 9, 45, 5, 47, 2, 2, 10]
This code should work:
def sort_integers(list_of_integers):
odd_numbers = [n for n in list_of_integers if n%2!=0]
odd_numbers = sorted(odd_numbers, reverse = True)
print(odd_numbers)
even_numbers = [x for x in list_of_integers if x%2 == 0]
even_numbers = sorted(even_numbers, reverse = True)
print(even_numbers)
new_sorted_list = even_numbers + odd_numbers
print(new_sorted_list)

Categories