-3

Link to original question

>>> theList = list(range(10))
>>> N = 3
>>> subList = [theList[n:n+N] for n in range(0, len(theList), N)]
>>> subList
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

How would I do this without list slicing?

Ahmad Khan
  • 17
  • 5

3 Answers3

1

Use the modulus operator %.

def split_by_n(n, lst):
  final = []
  sublist = []
  for i in range(0, len(lst)):
    if i % n != 0: # if i/n has no remainders
      sublist.append(lst[i])
    elif i != 0: # Reached the end of a sublist. Append to parent and reset.
      final.append(sublist)
      sublist = []
      sublist.append(lst[i])
    else: # 0 mod n is 0, so just make sure to add it anyways
      sublist.append(lst[i])
  final.append(sublist)
  return final
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
0

There are several methods:

You could use nested list comprehension with indexes into the list:

(extensive use of indexes is as unPythonic as it gets though)

theList = list(range(10))
N       = 3
L       = len(theList)
subList = [ [theList[j] for j in range(i,min(i+N,L))] for i in range(0,L,N) ]
print(subList)
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

You could also do it with itertools.groupby using an iterator for the grouping key, or itertools.islice( ,N) for each subranges:

from itertools import groupby

group   = iter(range(len(theList)))
subList = [ list(g) for _,g in groupby(theList,key=lambda _:next(group)//N) ]
print(subList)
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

from itertools import islice

iList   = iter(theList)
subList = [ list(islice(iList,N)) for _ in range(0,len(theList),N) ]
print(subList)
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

If you can't use libraries, you could use zip() to get chunks of the iterator in the list comprehension:

iList   = iter(theList)
subList = [[n for _,n in zip(range(N),iList)] for _ in range(0,len(theList),N)]
print(subList)
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

Or map with the next() function:

iList   = iter(theList)
subList = [ [f, *map(next,[iList]*(N-1))] for f in iList ]
print(subList)
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] 

Of course, there's always the good old for-loop method:

subList = [[]]
for n in theList:
    L,V = (subList[-1],n) if len(subList[-1])<N else (subList,[n]) 
    L.append(V)
print(subList)
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
Alain T.
  • 40,517
  • 4
  • 31
  • 51
0

Here's a (possibly over-kill) solution using itertools:

Step 1. convert theList into a generator:

gList = (x for x in theList)

Step 2. itertools.islice yields groups of N (forever):

import itertools as it
z1 = list(it.islice(gList,N)) # [0,1,2]
z2 = list(it.islice(gList,N)) # [3,4,5]
z3 = list(it.islice(gList,N)) # [6,7,8]
z4 = list(it.islice(gList,N)) # [9]
z5 = list(it.islice(gList,N)) # [], etc.

(Note we need to reset gList after this.)

Step 3. Wrap this up in another generator and use itertools.takewhile:

import itertools as it
gList = (x for x in theList)
subList = list(it.takewhile(lambda y: len(y) > 0, 
                            (list(it.islice(gList,N)) for _ in it.count(1))))

here it.count(1) is acting like a while True: loop

njp
  • 620
  • 1
  • 3
  • 16