Find elements in list with difference of 1 - python

Suppose I have a list like this:
lst = [1, 3, 4, 5, 8, 10, 14, 20, 21, 22, 23, 40, 47, 48]
I need to extract the elements which have a difference of one. I need final output to look like this:
[[3, 4, 5], [20, 21, 22, 23], [47, 48]]
Here is what I have tried so far for this particular problem which has yielded some progress but doesn't achieve 100% of what I need:
final_list = []
for i in range(len(lst)):
sub_list = []
for ii in range(i, len(lst)):
prev_num = lst[ii-1]
if lst[ii] - prev_num == 1:
# print(lst[ii], end=",")
sub_array.append(lst[ii])
else:
break
if sub_list:
final_list.append(sub_list)
print(final_list)
Output:
[[4, 5], [5], [21, 22, 23], [22, 23], [23], [48]]

You can use itertools.groupby to group the items with a key function that subtracts each item value with an incremental counter, which would result in a fixed value for each group of consecutive integers:
from itertools import groupby, count
lst = [1, 3, 4, 5, 8, 10, 14, 20, 21, 22, 23, 40, 47, 48]
c = count()
final_list = [g for _, [*g] in groupby(lst, lambda t: t - next(c)) if len(g) > 1]
final_list would become:
[[3, 4, 5], [20, 21, 22, 23], [47, 48]]
EDIT: If you'd rather have better performance than more concise code, you can look at #MadPhysicist's answer, which avoids overhead incurred from calling generator functions, and #don'ttalkjustcode's answer, which avoids creating lists until a pair of consecutive numbers are found. Combine the two answers and you get a solution that avoids both types of overhead:
out = []
prev = float('inf')
for i in lst:
if i - prev != 1:
current = None
elif current:
current.append(i)
else:
current = [prev, i]
out.append(current)
prev = i
Sample timing using #don'ttalkjustcode's benchmark code:
2716 μs Mad_Physicist
1554 μs dont_talk_just_code
1284 μs Mad_Physicist_x_dont_talk_just_code
Try it online!

You can step along the input list and start adding elements to an output list. Any time the difference is greater than 1, start a new output list. If the prior output is length 1, remove it:
out = []
current = []
prev = None
for i in lst:
if prev is None or i - prev != 1:
if len(current) > 1:
out.append(current)
current = []
current.append(i)
prev = i
if len(current) > 1:
out.append(current)

A zip solution that adds each first pair of a streak as an inner list and then appends further elements of the streak:
out = []
inner = None
for x, y in zip(lst, lst[1:]):
if y - x == 1:
if inner:
inner.append(y)
else:
inner = [x, y]
out.append(inner)
else:
inner = None
Benchmark with a random list 1000 as long as your example and with same density:
5172 μs blhsing
2697 μs Mad_Physicist
7667 μs Osman_Mamun
1571 μs dont_talk_just_code
Benchmark code (Try it online!):
from timeit import timeit
from itertools import groupby, count
from random import sample
def blhsing(lst):
c = count()
return [g for _, [*g] in groupby(lst, lambda t: t - next(c)) if len(g) > 1]
def Mad_Physicist(lst):
out = []
current = []
prev = None
for i in lst:
if prev is None or i - prev > 1:
if len(current) > 1:
out.append(current)
current = []
current.append(i)
prev = i
if len(current) > 1:
out.append(current)
return out
def Osman_Mamun(lst):
out = []
for k, g in groupby(enumerate(lst), key=lambda i: i[1]-i[0]):
temp = list(g)
if len(temp) > 1:
out.append([i[1] for i in temp])
return out
def dont_talk_just_code(lst):
out = []
inner = None
for x, y in zip(lst, lst[1:]):
if y - x == 1:
if inner:
inner.append(y)
else:
inner = [x, y]
out.append(inner)
else:
inner = None
return out
lst_example = [1, 3, 4, 5, 8, 10, 14, 20, 21, 22, 23, 40, 47, 48]
funcs = blhsing, Mad_Physicist, Osman_Mamun, dont_talk_just_code
for _ in range(3):
lst = sorted(sample(range(max(lst_example) * 1000), len(lst_example) * 1000))
# lst = lst_example
results = []
for func in funcs:
t = timeit(lambda: results.append(func(lst)), number=100) / 100
print('%d μs ' % (t * 1e6), func.__name__)
assert all(result == results[0] for result in results), (list(map(len, results)), results)
print()

Using itertools.groupby:
In [19]: out = []
In [20]: for k, g in groupby(enumerate(lst), key=lambda i: i[1]-i[0]):
...: temp = list(g)
...: if len(temp) > 1:
...: out.append([i[1] for i in temp])
...:
In [21]: out
Out[21]: [[3, 4, 5], [20, 21, 22, 23], [47, 48]]

You can go through the elements and either add them as a new group or add them to the last group depending on whether it is one more than the last one added. When done, remove the single element groups.
lst = [1, 3, 4, 5, 8, 10, 14, 20, 21, 22, 23, 40, 47, 48]
groups = []
for n in lst:
if groups and n-1 == groups[-1][-1]:
groups[-1].append(n) # +1 element, add to last group
else:
groups.append([n]) # add as a new group
groups = [g for g in groups if len(g)>1] # remove single element groups
print(groups)
[[3, 4, 5], [20, 21, 22, 23], [47, 48]]

Related

Python fetch all consecutive odd or even numbers

Given a list of integers, how can I group together consecutive numbers that share the same parity?
Here is an example input:
[5, 9, 11, 20, 24, 30, 31, 33, 39, 41]
And here is an example output:
[[5, 9, 11], [20, 24, 30], [31, 33, 39, 41]]
a simple loop does the job:
test = [5,7,9,11,13,20,22,24,31,33,39,41,43,44,46,50,52]
result = []
res = []
prevalue = None
for v in test:
if prevalue == None or v == prevalue + 2:
prevalue = v
res.append(v)
else:
prevalue = v
result+= [res]
res = [v]
result+= [res]
print(result)
result:
[[5, 7, 9, 11, 13], [20, 22, 24], [31, 33], [39, 41, 43], [44, 46], [50, 52]]
You can use itertools.groupby() to produce the groupings of odd / even numbers:
[list(group) for _, group in groupby(data, lambda x: x % 2 == 0)]
This outputs:
[[5, 9, 11], [20, 24, 30], [31, 33, 39, 41]]
Basically, what you want to do is track the current list you're looking at and modify it as you go and, when your condition no longer holds true, you save the list and create a new one:
results = []
current = [data[0]]
flag = data[0] % 2
for number in data[1:]:
if number % 2 == flag:
current.append(number)
else:
flag = 1 - flag
results.append(current)
current = [number]
results.append(current)

Loop of List into a Tabbed New List

I am a high school student. By observing the inputted sequence, I want to create a program that creates general formulas on quadratic to a polynomial with nth power equations. This posted code will function as a means of validation for inputted sequences. If you have a better code, please help me. Thank You!
The same concept was shown below,
here is my code
def shrink(numbers):
n1 = [(x, numbers[i+1]) for i, x in enumerate(numbers)
if i < len(numbers)-1]
n2 = [x[1]-x[0] for x in n1]
print(n2)
if(len(n2) > 1):
return shrink(n2)
return n
#shrink([1, 8, 27, 64, 125, 216])
a = input()
b = a.split()
for i in range(len(b)):
b[i] = int(b[i])
shrink(b)
"""
The output will be:
[7, 19, 37, 61, 91]
[12, 18, 24, 30]
[6, 6, 6]
[0, 0]
[0]
"""
#I want the output from the top to be like this!
d = [
[7, 19, 37, 61, 91],
[12, 18, 24, 30],
[6, 6, 6],
[0, 0],
[0]
]
if d[2][0] == d[2][1]:
print('cubic sequence')
During the first call you initialize a list variable which you update and pass to the recursive call
def shrink(numbers,return_list = []):
n1 = [(x, numbers[i+1]) for i, x in enumerate(numbers) if i < len(numbers)-1]
n2 = [x[1]-x[0] for x in n1]
return_list.append(n2)
if(len(n2) > 1):
return shrink(n2,return_list)
else:
return return_list
print(shrink([1, 8, 27, 64, 125, 216]))
If you want the values of n1 as well :
def shrink(numbers,n1_list = [], n2_list = []):
n1 = [(x, numbers[i+1]) for i, x in enumerate(numbers) if i < len(numbers)-1]
n2 = [x[1]-x[0] for x in n1]
n1_list.append(n1)
n2_list.append(n2)
# print(n2)
if(len(n2) > 1):
return shrink(n2,n1_list,n2_list)
else:
return n1_list,n2_list
print(shrink([1, 8, 27, 64, 125, 216]))
Thank You Atharva Gundawar.
If you want the list to be inputted, this is the answer:
Take note that input list should be separated by space and not by a comma.
Sample input: 1 8 27 64 125 216
def shrink(numbers, return_list=[]):
n1 = [(x, numbers[i + 1]) for i, x in enumerate(numbers) if i < len(numbers) - 1]
n2 = [x[1] - x[0] for x in n1]
return_list.append(n2)
if (len(n2) > 1):
return shrink(n2, return_list)
else:
return return_list
a = input()
b = a.split()
for i in range(len(b)):
b[i] = int(b[i])
c = shrink(b)
print(shrink(b))
print(c[2][0])
print(c[2][1])
if c[2][0] == c[2][1]:
print('cubic sequence')
Input:
1 8 27 64 125 216
Output:
[[7, 19, 37, 61, 91], [12, 18, 24, 30], [6, 6, 6], [0, 0], [0], [7, 19, 37, 61, 91], [12, 18, 24, 30], [6, 6, 6], [0, 0], [0]]
6
6
cubic sequence
This is the correct answer to eradicate the loop:
https://stackoverflow.com/posts/70423499/revisions
def shrink(numbers, return_list=[]):
n1 = [(x, numbers[i + 1]) for i, x in enumerate(numbers) if i < len(numbers) - 1]
n2 = [x[1] - x[0] for x in n1]
return_list.append(n2)
if (len(n2) > 1):
return shrink(n2, return_list)
else:
return return_list
input_user = input("Enter data:")
b = input_user.split()
for num in range(len(b)):
b[num] = int(b[num])
c = shrink(b)
print(c)

How can I transform a list in a list of lists if I need to skip some characters in between?

Let lst = [13, 1, 14, -64, 9, -64, 14, 5]
How can I create a list of lists without taking into account the negative number ?
Result would be [[13,1,14], [9], [14,5]]
lst = [13, 1, 14, -64, 9, -64, 14, 5]
lst_index = []
for i,v in enumerate(lst):
if lst[i] == -64:
lst_index.append(i)
So this is what I have done to retrieve the index of each negative number. Now I should remove it from the list and create list of lists but how?? Thank you !
Using itertools.groupby:
from itertools import groupby
lst = [13, 1, 14, -64, 9, -64, 14, 5]
res = [list(group) for key, group in groupby(lst, lambda x : x > 0) if key]
print(res)
Output
[[13, 1, 14], [9], [14, 5]]
Or simply:
current, res = [], []
for e in lst:
if e < 0:
if current:
res.append(current)
current = []
else:
current.append(e)
if current:
res.append(current)
print(res)
Output
[[13, 1, 14], [9], [14, 5]]
You could do something like this:
def group_positives(lst):
result = []
temp = []
for value in lst:
if value > 0:
temp.append(value)
else:
if temp:
result.append(temp)
temp = []
# Also append last temp if not empty
if temp:
result.append(temp)
return result
One solution: every time you find a negative number add to the new list an empty list, and after that append the positive numbers to the last element of the new list, see below.
lst = [13, 1, 14, -64, 9, -64, 14, 5]
new_lst = []
start_lst = True
for v in lst :
if v < 0:
start_lst = True
else:
if start_lst == True :
start_lst = False
new_lst.append([])
new_lst[-1].append(v)
print(new_lst)
What I do here is just adding The numbers to a New list till I find a negative number if a Negative number met The else Condition
1-Stop
2-Add the List you have to COMBO
3-Jump over this number by index+1
the last iteration will not add the last list because there is no negative number to Stop and do the 3 steps so I added -1 at the end to go to else
Summary
Add Numbers to the list till you find a negative number then add this list to combo
in the end, you will have the list but you will not add it combo as you didn't face a negative number ( so I added -1 )
lst = [13, 1, 14, -64, 9, -64, 14, 5]
def Test(V):
V.append(-1)
new = []
Combo = []
for index, value in enumerate(V):
if(lst[index] > 0):
new.append(lst[index])
else:
index += 1
Combo.append(new)
new = list()
return Combo
z = Test(lst)
print(z)
Output
[[13, 1, 14], [9], [14, 5]]

Python array logic

I am trying to create a list of lists with the input of m and n, where m is the number of lists within the main list and n is the number of elements within each given list. The grid should contain the integers from start to start + rows * cols - 1 and be ascending. But, every odd numbered row should be descending instead.
The code I've written is returning the expected results, but my automated tester is saying it's incorrect. Maybe my logic is messed up somewhere?
inputs:
start = 1, m = 3, n = 5
expected:
[[1,2,3,4,5],[10,9,8,7,6],[11,12,13,14,15]]
result = []
mylist = []
start = 1
for x in range(0, rows):
for x in range(0, cols):
result.append(start)
start += 1
for y in range(0, rows):
if y%2 != 0:
mylist.append(result[cols - 1::-1])
del result[cols - 1::-1]
else:
mylist.append(result[0:cols])
del result[0:cols]
return mylist
One possible solution, using itertools.count:
from itertools import count
def build(m, n, start=1):
lst, c = [], count(start)
for i in range(m):
lst.append([next(c) for j in range(n)][::-1] if i % 2 else [next(c) for j in range(n)])
return lst
print(build(3, 5, 1))
Prints:
[[1, 2, 3, 4, 5], [10, 9, 8, 7, 6], [11, 12, 13, 14, 15]]
print(build(3, 0, 1))
Prints:
[[], [], []]
just generate the list of numbers you need which will be n * m, in your case that would generate 0 to 14 in the python range function. However as we want to start at ` then we need to add the start offset too the range end.
Now we can generate all the numbers we need we just need to think about how to create them.
well we can add numbers to the list until the list reaches the size of n, then we need to start a new list, However if the list we just finished is an even numbered row then we need to reverse that list.
def build_lists(m, n, start=1):
data =[[]]
for i in range(start, n * m + start):
if len(data[-1]) < n:
data[-1].append(i)
else:
if len(data) % 2 == 0:
data[-1] = data[-1][::-1]
data.append([i])
if len(data) % 2 == 0:
data[-1] = data[-1][::-1]
return data
print(build_lists(3, 5))
print(build_lists(6, 3))
print(build_lists(6, 2, 100))
OUTPUT
[[1, 2, 3, 4, 5], [10, 9, 8, 7, 6], [11, 12, 13, 14, 15]]
[[1, 2, 3], [6, 5, 4], [7, 8, 9], [12, 11, 10], [13, 14, 15], [18, 17, 16]]
[[100, 101], [103, 102], [104, 105], [107, 106], [108, 109], [111, 110]]

How to return 2 separate lists from a single list?

If have this list here:
[25, 8, 22, 9]
How can I make the program create 2 seperate lists, and print them both? One should contain all the numbers less than 20, and the other needs to contain all the numbers greater than 20. The final print result should be displayed like this: [8, 9], [25, 22]
>>> predicates = lambda x:x<20, lambda x:x>20
>>> print [filter(pred, [25, 8, 22, 9]) for pred in predicates]
[[8, 9], [25, 22]]
Use list comprehensions:
>>> L = [25, 8, 22, 9]
>>> [x for x in L if x < 20]
[8, 9]
>>> [x for x in L if x > 20]
[25, 22]
def print_split_list(raw_list, split_value):
lower_list = [v for v in raw_list if v < split_value]
upper_list = [v for v in raw_list if v >= split_value]
print lower_list, upper_list
print_split_list([25, 8, 22, 9], 20) # => [8, 9] [25, 22]
a = [25, 8, 22, 9]
print [x for x in a if x > 20]
print [x for x in a if x < 20]
You use here list comprehensions. List comprehension looks like:
[ f(x) for x in a if cond(x) ]
That means: produce me a list that consists of f(x) for each element of x for that cond(x) is True.
In our case f(x)is simply x. And cond(x) is x > 20 or x < 20 (please note also that if you have 20s in your list, they will disappear from the result).
If it is a homework you can solve the task in more low-level way:
a = [25, 8, 22, 9]
list1 = []
list2 = []
for elem in a:
if elem > 20:
list1.append(elem)
if elem < 20:
list2.append(elem)
print list1
print list2
Here you iterate through the list and check its elements.
That elements that are greater than 20 you append to one list; and that that are lesser thatn 20 — to the other.
Note: the assumes you want twenty in listTwo
listOne = [x for x in yourList if x < 20]
listTwo = [x for x in yourList if x >= 20]
print listOne
print listTwo
Although you should use list comprehensions you might be interested in the for loop approach if you are starting with python
listOne = []
listOne = []
for x in yourList:
if x < 20:
listOne.append(x)
else:
listTwo.append(x)
li = [25, 8, 22, 9]
li.sort()
for i, x in enumerate(li):
if x > 20:
print li[:i]
print li[i:]
break

Categories