Break a list into pieces based on a set of indexes in Python

What is the best way to break a list into pieces based on an arbitrary number of indexes? For example. given the code below

indexes = [5, 12, 17] list = range(20) 

bring back something like this

 part1 = list[:5] part2 = list[5:12] part3 = list[12:17] part4 = list[17:] 

If there are no indexes, it should return the whole list.

+49
python list
Jul 29 '09 at 7:16
source share
9 answers

This is the simplest and most pythonic solution I can think of:

 def partition(alist, indices): return [alist[i:j] for i, j in zip([0]+indices, indices+[None])] 

if the inputs are very large, then the iterator solution should be more convenient:

 from itertools import izip, chain def partition(alist, indices): pairs = izip(chain([0], indices), chain(indices, [None])) return (alist[i:j] for i, j in pairs) 

and, of course, a very, very lazy solution for guys (if you don't mind getting arrays instead of lists, but in any case you can always return them to lists):

 import numpy partition = numpy.split 
+47
Jul 29 '09 at 8:55
source share

I would be interested to see a more Pythonic way of doing this as well. But this is a shitty decision. You need to add validation for an empty index list.

Something like:

 indexes = [5, 12, 17] list = range(20) output = [] prev = 0 for index in indexes: output.append(list[prev:index]) prev = index output.append(list[indexes[-1]:]) print output 

produces

 [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9, 10, 11], [12, 13, 14, 15, 16], [17, 18, 19]] 
+8
Jul 29 '09 at 7:28
source share

My solution is similar to Il-Bhima's.

 >>> def parts(list_, indices): ... indices = [0]+indices+[len(list_)] ... return [list_[v:indices[k+1]] for k, v in enumerate(indices[:-1])] 

Alternative approach

If you want to slightly change the way indexes are entered, from absolute indices to relative (that is, from [5, 12, 17] to [5, 7, 5] , below you will also get the desired result, while it does not create intermediate lists .

 >>> from itertools import islice >>> def parts(list_, indices): ... i = iter(list_) ... return [list(islice(i, n)) for n in chain(indices, [None])] 
+7
Jul 29 '09 at 7:35
source share
 >>> def burst_seq(seq, indices): ... startpos = 0 ... for index in indices: ... yield seq[startpos:index] ... startpos = index ... yield seq[startpos:] ... >>> list(burst_seq(range(20), [5, 12, 17])) [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9, 10, 11], [12, 13, 14, 15, 16], [17, 18, 19]] >>> list(burst_seq(range(20), [])) [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]] >>> list(burst_seq(range(0), [5, 12, 17])) [[], [], [], []] >>> 

Maxima mea culpa: it uses a for statement, and it does not use whizzbang things like itertools, zip (), None as a sentinel, lists, ...

; -)

+4
Jul 30 '09 at 3:32
source share
 indices = [5, 12, 17] input = range(20) output = [] reduce(lambda x, y: output.append(input[x:y]) or y, indices + [len(input)], 0) print output 
+2
Jul 29 '09 at 8:24
source share

Thatโ€™s all I could think of.

 def partition(list_, indexes): if indexes[0] != 0: indexes = [0] + indexes if indexes[-1] != len(list_): indexes = indexes + [len(list_)] return [ list_[a:b] for (a,b) in zip(indexes[:-1], indexes[1:])] 
0
Jul 29 '09 at 7:32
source share

Cide makes three copies of the array: [0] + indexes copy ([0] + indexes) + [] copy again, and indexes [: - 1] copy the third time. Il-Bhima is five copies. (Of course, I do not consider the return value.)

Those can be reduced (izip, islice), but here is the version with a zero copy:

 def iterate_pairs(lst, indexes): prev = 0 for i in indexes: yield prev, i prev = i yield prev, len(lst) def partition(lst, indexes): for first, last in iterate_pairs(lst, indexes): yield lst[first:last] indexes = [5, 12, 17] lst = range(20) print [l for l in partition(lst, indexes)] 

Of course, copies of arrays are pretty cheap (native code) compared to interpreted Python, but this has another advantage: it is easy to reuse it to mutate data directly:

 for first, last in iterate_pairs(lst, indexes): for i in range(first, last): lst[i] = first print lst # [0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 12, 12, 12, 12, 12, 17, 17, 17] 

(That's why I passed the iterate_pairs indexes. If you donโ€™t care, you can remove this parameter and just have the final line: "yield prev, None", which all sections need ().

0
Jul 29 '09 at 8:20
source share

Here is another answer.

 def partition(l, indexes): result, indexes = [], indexes+[len(l)] reduce(lambda x, y: result.append(l[x:y]) or y, indexes, 0) return result 

It supports negative indices, etc.

 >>> partition([1,2,3,4,5], [1, -1]) [[1], [2, 3, 4], [5]] >>> 
0
Jul 29 '09 at 16:31
source share

Many indexes are indexes. Transition to simplicity / readability.

 indices = [5, 12, 17] input = range(20) output = [] for i in reversed(indices): output.append(input[i:]) input[i:] = [] output.append(input) while len(output): print output.pop() 
-one
Jul 29 '09 at 7:43
source share



All Articles