I\'m new in Python but basically I want to create sub-groups of element from the list with a double loop, therefore I gonna compare the first element with the next to figure
@uselpa's version is fine. Here's mine (same issue with [50] instead of just 50) that uses collections.deque
to be a little more efficient, and also some long comments...
#! /usr/bin/env python
from collections import deque
def process(lst):
"""
Given a list of values that is not sorted (such that for some
valid indices i,j, i<j, sometimes lst[i] > lst[j]), produce a
new list-of-lists, such that in the new list, each sublist *is*
sorted:
for all sublist \elem returnval:
assert_is_sorted(sublist)
and furthermore this is the minimal set of sublists required
to achieve the condition.
Thus, if the input list lst is actually sorted, this returns
[list(lst)].
"""
def sublist(deq):
"""
Pop items off the front of deque deq until the next one
goes backwards. Return the constructed sub-list.
"""
sub = [deq.popleft()]
while deq and deq[0] >= sub[-1]:
sub.append(deq.popleft())
return sub
# Make a copy of lst before modifying it; use a deque so that
# we can pull entries off it cheaply.
deq = deque(lst)
output = []
ret = []
while deq:
ret.append(sublist(deq))
return ret
print process([45,78,120,47,58,50,32,34])
(Incidentally, in the days before collections.deque
I'd probably just use a reversed copy of lst
and use lst.pop()
in sublist
. That's not quite as obvious, though.)
I used a double loop as well, but put the inner loop in a function:
#!/usr/bin/env python
def process(lst):
def prefix(lst):
pre = []
while lst and (not pre or pre[-1] <= lst[0]):
pre.append(lst[0])
lst = lst[1:]
return pre, lst
res=[]
while lst:
subres, lst = prefix(lst)
res.append(subres)
return res
print process([45,78,120,47,58,50,32,34])
=> [[45, 78, 120], [47, 58], [50], [32, 34]]
The prefix function basically splits a list into 2; the first part is composed of the first ascending numbers, the second is the rest that still needs to be processed (or the empty list, if we are done).
The main function then simply assembles the first parts in a result lists, and hands the rest back to the inner function.
I'm not sure about the single value 50; in your example it's not in a sublist, but in mine it is. If it is a requirement, then change
res.append(subres)
to
res.append(subres[0] if len(subres)==1 else subres)
print process([45,78,120,47,58,50,32,34])
=> [[45, 78, 120], [47, 58], 50, [32, 34]]
No loop! Well at least, no explicit looping...
import itertools
def process(lst):
# Guard clause against empty lists
if len(lst) < 1:
return lst
# use a dictionary here to work around closure limitations
state = { 'prev': lst[0], 'n': 0 }
def grouper(x):
if x < state['prev']:
state['n'] += 1
state['prev'] = x
return state['n']
return [ list(g) for k, g in itertools.groupby(lst, grouper) ]
Usage (work both with Python 2 & Python 3):
>>> data = [45,78,120,47,58,50,32,34]
>>> print (list(process(data)))
[[45, 78, 120], [47, 58], [50], [32, 34]]
Joke apart, if you need to group items in a list itertools.groupby deserves a little bit of attention. Not always the easiest/best answer -- but worth to make a try...
EDIT: If you don't like closures -- and prefer using an object to hold the state, here is an alternative:
class process:
def __call__(self, lst):
if len(lst) < 1:
return lst
self.prev = lst[0]
self.n = 0
return [ list(g) for k, g in itertools.groupby(lst, self._grouper) ]
def _grouper(self, x):
if x < self.prev:
self.n += 1
self.prev = x
return self.n
data = [45,78,120,47,58,50,32,34]
print (list(process()(data)))
EDIT2: Since I prefer closures ... but @torek don't like the dictionary syntax, here a third variation around the same solution:
import itertools
def process(lst):
# Guard clause against empty lists
if len(lst) < 1:
return lst
# use an object here to work around closure limitations
state = type('State', (object,), dict(prev=lst[0], n=0))
def grouper(x):
if x < state.prev:
state.n += 1
state.prev = x
return state.n
return [ list(g) for k, g in itertools.groupby(lst, grouper) ]
data = [45,78,120,47,58,50,32,34]
print (list(process(data)))
why not simply use a single for loop instead of using itertool and all fancy stuff.
def list_test(inlist, outlist):
if not inlist:
return []
prev=inlist[0]
x=[]
for item in inlist:
if item >= prev:
x.append(item)
else:
outlist.append(x)
x=[]
x.append(item)
prev=item
if x:
outlist.append(x)
in_list=[1,0,1,2,3]
out_list = []
list_test(in_list, out_list)
print(out_list)
O/p:[[1], [0, 1, 2, 3]]