Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I have the following lists and I'd like to get the consecutive integers from all the list.
list = [[3, 10, 15, 88],
[4, 11, 30],
[1, 6, 12, 50, 74]]
It should return [10, 11, 12].
If there are more than one consecutive list like
list = [[3, 10, 15, 88],
[4, 11, 30],
[5, 6, 12, 50, 74]]
It should return both [3,4,5] and [10,11,12].
I'd like to know the most efficient solution. Thank you.
l = [[3, 10, 15, 88],
[4, 11, 30],
[5, 6, 12, 50, 74]]
result = [[elem-1,elem,elem+1] for elem in l[1] if (elem+1) in l[2] and (elem-1) in l[0] ]
[[3, 4, 5], [10, 11, 12]]
for elem in l[1] : we are going to iterate over middle list and check if elem+1in the next list and elem-1 in previous list
Try this in just one line using product:
from itertools import product
l = [[3, 10, 15, 88],
[4, 11, 30],
[5, 6, 12, 50, 74]]
result = [[i,j,k] for i,j,k in product(*l) if k==j+1 and j==i+1]
The result will be:
In [7]: result
Out[7]: [[3, 4, 5], [10, 11, 12]]
Note that: Do not use list as a variable name. it is predefined in python and you will override its main functionality.
you can simply do this to get the required answer
from operator import itemgetter
data = [ 1,4,5,6,10,15,16,17,18,22,25,26,27,28]
for k, g in groupby(enumerate(data), lambda (i, x): i-x):
print map(itemgetter(1), g)
Result:
[1]
[4, 5, 6]
[10]
[15, 16, 17, 18]
[22]
[25, 26, 27, 28]
with simple reasoning
l = [[3, 10, 15, 88],
[4, 11, 30],
[5, 6, 12, 50, 74]]
l2 = [(index,j) for index,i in enumerate(l) for j in i] # flattening the list
l2 = sorted(l2, key= lambda x: x[1]) # sorting the list
# the sorted list has the below structure (sublist_number, item)
output = []
current_group = []
temp = []
for i in l2:
if(len(temp) == 0):
temp.append(i[1])
current_group.append(i[0])
else:
if(i[1] - temp[-1] != 1): # that means not consecutive
if(len(temp) == 3):
output.append(temp.copy())
temp.clear()
current_group.clear()
temp.append(i[1])
current_group.append(i[0])
else:
if(i[0] not in current_group):
temp.append(i[1])
current_group.append(i[0])
print(output)
[[3, 4, 5], [10, 11, 12]]
Related
This question already has answers here:
group together consecutive numbers in a list
(2 answers)
Closed 6 months ago.
I have a list of numbers:
lst = [0, 1, 3, 4, 6, 9, 10, 11, 12, 13, 15]
and I would like to get a list of lists like so:
[[0, 1], [3, 4], [6], [9, 10, 11, 12, 13], [15]]
this means I would like to get the available continuous unique sequences from the first list.
What have I tried?
I have clunky function which iterates over each element and checks if the element is equal to the previous element +1 and appends it if that was the case.
You could try something like this:
def sequences(lst):
ret = [] # Initialise return list
j = 0 # Index at which the last break was made
for i, n in enumerate(lst[1:]): # Iterate through indexes and numbers
if n != (lst[i] + 1): # Check if there is a step which is not 1
ret.append(lst[j:i+1]) # If there is, append this portion to ret
j = i+1 # Update j
ret.append(lst[j:]) # Add the last portion
return ret # Return
print(sequences([0, 1, 3, 4, 6, 9, 10, 11, 12, 13, 15]))
Output: [[0, 1], [3, 4], [6], [9, 10, 11, 12, 13], [15]]
self-explanatory code:
def notSoClunkyFunc(lst):
if len(lst)==0: return [] #returns if empty list
ans=[] #The final result you want
temp=[lst[0]] #the temporary list that will be added to the ans as per the question demands.
for i in range(1,len(lst)): #Loop that works on the same logic as suggested by you along with the question.
if lst[i]==temp[-1]+1:
temp.append(lst[i])
else:
ans.append(temp)
temp=[]
temp.append(lst[i])
if len(temp): ans.append(temp)
return ans
lst = [0, 1, 3, 4, 6, 9, 10, 11, 12, 13, 15]
print(notSoClunkyFunc(lst))
Output:
[[0, 1], [3, 4], [6], [9, 10, 11, 12, 13], [15]]
Say I have a list that contains 5 unique integers in the range of 0 to 9.
import random
lst = random.sample(range(10), 5)
I also have a list of lists, which is obtained by splitting integers from 0 to 19 into 6 groups:
partitions = [[8, 12], [2, 4, 16, 19], [1, 6, 7, 13, 14, 17], [3, 15, 18], [5, 9, 10, 11], [0]]
Now I want to split lst based on the reference partitions.
For example, if I have
lst = [0, 1, 6, 8, 9]
I expect the output to be a list of lists like this:
res = [[0], [1, 6], [8], [9]]
I want the algorithm to be as fast as possible. Any suggestions?
res=[]
for sublist in partitions: # go through all sublists in partitions
match = [i for i in lst if i in sublist] # find matching numbers in sublist and lst
if match: # if it is empty don't append it to res
res.append(match)
# at this point res is [[8], [1, 6], [9], [0]]
print(sorted(res)) # use sorted to get desired output
I don't know if this is the fastest algorithm but it works
import random
lst = random.sample(range(10), 5)
partitions = [[8, 12], [2, 4, 16, 19], [1, 6, 7, 13, 14, 17], [3, 15, 18], [5, 9, 10, 11], [0]]
sequence = []
result = []
for i in range(5):
for j in range(len(partitions)):
if lst[i] in partitions[j]:
if j in sequence:
where = sequence.index(j)
result[where] += [lst[i]]
else:
result += [[lst[i]]]
sequence += [j]
break
print(result)
I have a list of lists in python:
[[1],[2],[3,4],[5,6],[7,8,9,10,11],[12,13,14,15,16],[17]]
I would like to combine the sublists into a single sublist if they hold the same number of elements:
[[1,2,17],[3,4,5,6],[7,8,9,10,11,12,13,14,15,16]]
Is there a simple way of doing this?
Use groupby and chain from itertools
Ex:
from itertools import groupby, chain
lst = [[1],[2],[3,4],[5,6],[7,8,9,10,11],[12,13,14,15,16],[17]]
result = [list(chain.from_iterable(v)) for k, v in groupby(sorted(lst, key=lambda h: len(h)), lambda x: len(x))]
print(result)
Output:
[[1, 2, 17], [3, 4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15, 16]]
sorted(lst, key=lambda h: len(h)) to sort your list by len
then use groupby to group your list by len of list
A "simpler" approach without itertools:
dictByLength = {}
for i in mylist:
dictByLength[len(i)] = dictByLength.get(len(i), []) + i
print(list(dictByLength.values()))
output:
[[1, 2, 17], [3, 4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15, 16]]
Here is my approach (without using itertools):
l = [[1],[2],[3,4],[5,6],[7,8,9,10,11],[12,13,14,15,16],[17]]
# create one sublist for each possible length
m = [[] for size in range(len(max(l, key=len)))]
# append to each size-sublist, the appropriate sublist
for sub_l in l:
size = len(sub_l)
m[size - 1] += sub_l
# remove empty sub lists
m = [sub_m for sub_m in m if sub_m]
print(m)
[[1, 2, 17], [3, 4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15, 16]]
input:
a sorted list, like this:[1,2,3,8,10,15,16,17,18,22,23,27,30,31]
a threshold, like this: max_diff = 2
expected output:
a list of sub lists; each sub list contains the values that the neighboring difference is smaller than max_diff, like this: [[1, 2, 3], [8, 10], [15, 16, 17, 18], [22, 23], [27], [30, 31]]
Here's how I did this, I am wondering if there is a better way to do this.
test_list = [1,2,3,8,10,15,16,17,18,22,23,27,30,31]
max_diff = 2
splited_list = []
temp_list = [test_list[0]]
for i in xrange(1,len(test_list)):
if test_list[i] - temp_list[-1] > max_diff:
splited_list.append(temp_list)
temp_list = [test_list[i]]
else:
temp_list.append(test_list[i])
if i == len(test_list) -1:
splited_list.append(temp_list)
print splited_list
You can use enumerate and zip function within a list comprehension to find the indices of the elements that value difference is larger than 2, then split your list based on index list :
>>> li =[1, 2, 3, 8, 10, 15, 16, 17, 18, 22, 23, 27, 30, 31]
>>> inds=[0]+[ind for ind,(i,j) in enumerate(zip(li,li[1:]),1) if j-i>2]+[len(li)+1]
>>> [li[i:j] for i,j in zip(inds,inds[1:])]
[[1, 2, 3], [8, 10], [15, 16, 17, 18], [22, 23], [27], [30, 31]]
>>> a = [1,2,3,8,10,15,16,17,18,22,23,27,30,31]
>>> b = a[1:] #offset by 1 position
>>> b
[2, 3, 8, 10, 15, 16, 17, 18, 22, 23, 27, 30, 31]
>>> c = [(i[1] - i[0]) for i in zip(a[:-1], b)]
>>> c #position diff
[1, 1, 5, 2, 5, 1, 1, 1, 4, 1, 4, 3, 1]
>>> d = [i[0] for i in enumerate(c) if i[1] > 2]
>>> d #split position
[2, 4, 8, 10, 11]
>>> e = [-1]+d+[len(a)]
>>> e #add start end to split position
[-1, 2, 4, 8, 10, 11, 14]
>>> [a[l[0]+1: l[1]+1] for l in zip(e, e[1:])]
[[1, 2, 3], [8, 10], [15, 16, 17, 18], [22, 23], [27], [30, 31]]
#split result
Rearranging your lines leads to a more compact form:
test_list = [1,2,3,8,10,15,16,17,18,22,23,27,30,31]
max_diff = 2
splited_list = []
prev_element = float('-inf')
for element in test_list:
if element - prev_element > max_diff:
splited_list.append([])
splited_list[-1].append(element)
prev_element = element
print splited_list
Works on all iterables
def split_by_threshold(seq, max_diff=2):
it = iter(seq)
last = next(it)
part = [last]
for curr in it:
if curr - last > max_diff:
yield part
part = []
part.append(curr)
last = curr
yield part
l = [1,2,3,8,10,15,16,17,18,22,23,27,30,31]
print(list(split_by_threshold(l)))
I have a list of lists like this: [[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]].
I want to write a function that will return: [16, 14, 12, 7, 6]: i.e. the last 5 elements in the list of lists.
This is the code I have, but it is not very pythonic at all (master_list contains the list above):
def find_last_five():
last_five = []
limit = 5
for sublist in reversed(master_list):
# have to check that list is not None.
if sublist:
for elem in sublist:
last_five.append(elem)
limit -= 1
if (limit == 0):
return last_five
return last_five
import itertools as it
a = [[1, 2], [4, 5, 6], [], [7, 12, 14, 16]]
reversed(it.islice(it.chain.from_iterable(reversed(a)), 5))
That actually assumes there are no None's in a. If there are just do a = filter(a, None).
Given your example; I will assume your items in your list are either an iterable or None;
>>> import itertools
>>> lst = [[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]]
>>> print list(itertools.chain(*[l for l in lst if l is not None]))[-5:]
[6, 7, 12, 14, 16]
You can use a list comprehension:
>>> tgt=[[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]]
>>> [e for sub in tgt if sub for e in sub][-5:]
[6, 7, 12, 14, 16]
That filters out the None. To filter out other non-list or tuples:
>>> [e for sub in tgt if isinstance(sub, (list, tuple)) for e in sub][-5:]
If you want something that does not have to flatten the entire list of lists first, you can just deal with the structure from the end and move up until you have what you want:
result=[]
current=[]
it=reversed(tgt)
while len(result)<5:
if current:
result.append(current.pop())
continue
else:
try:
current=next(it)
except StopIteration:
break
(Or use John 1024's solution)
Using no external modules:
master = [[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]]
new = []
total = 5
for x in reversed(master):
if x:
new += list(reversed(x))[:total-len(new)]
if total == len(new):
break
print(new)
This produces:
[16, 14, 12, 7, 6]
which is the desired list with the elements in the desired order.
Alternative approach using flatten recipe:
import collections
l = [[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]]
def flatten(l):
for el in l:
if isinstance(el, collections.Iterable) and not isinstance(el, str):
for sub in flatten(el):
yield sub
else:
yield el
print([v for v in flatten(l) if v][-5:])
# gives: [6, 7, 12, 14, 16]
How about a different approach?
a = [[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]]
sum(filter(None, a), [])[-1:-6:-1]
The filter function is necessary only because of the None type in the list. In case it is just a list of lists, this will be lot simpler to write like this:
sum(a, [])[-1:-6:-1]
The principle behind this? We actually use the '+' operator of list to just keep on adding the lists into a single list. Please note that this is not the way to choose(if you choose ;)) for longer lists. For smaller and medium lists, this is fine.
I'd use itertools to do this. Something like
list(itertools.chain.from_iterable(x for x in l if x is not None))[:-5]
where l is your input list.