Scotty Allen
Scotty Allen

Reputation: 13447

What's the best python idiom for grouping a list of items into groups of a specific max size?

I want to write a function that takes items from a list and groups them into groups of size n.

Ie, for n = 5, [1, 2, 3, 4, 5, 6, 7] would become [[1, 2, 3, 4, 5], [6, 7]].

What's the best python idiomatic way to do this?

Upvotes: 2

Views: 312

Answers (4)

taleinat
taleinat

Reputation: 8721

Solutions using ranges with steps only work on sequences such as lists and tuples (not iterators). They also aren't as efficient as they can be, since they access the sequence many times instead of iterating over it once.

Here's a version which supports iterators and only iterates over the input once, creating a list of lists:

def blockify(iterator, blocksize):
    """Split the items in the given iterator into blocksize-sized lists.

    If the number of items in the iterator doesn't divide by blocksize,
    a smaller block containing the remaining items is added to the result.

    """
    blocks = []
    for index, item in enumerate(iterator):
        if index % blocksize == 0:
            block = []
            blocks.append(block)
        block.append(item)
    return blocks

And now an iterator version which returns an iterator of tuples, doesn't have a memory overhead, and allows choosing whether to include the remainder. Note that the output can be converted into a list via list(blockify(...)).

from itertools import islice

def blockify(iterator, blocksize, include_remainder=True):
    """Split the items in the given iterator into blocksize-sized tuples.

    If the number of items in the iterator doesn't divide by blocksize and
    include_remainder is True, a smaller block containing the remaining items
    is added to the result; if include_remainder is False the remaining items
    are discarded.

    """
    iterator = iter(iterator) # we need an actual iterator
    while True:
        block = tuple(islice(iterator, blocksize))
        if len(block) < blocksize:
            if len(block) > 0 and include_remainder:
                yield block
            break
        yield block

Upvotes: 1

Matt Boehm
Matt Boehm

Reputation: 1912

I don't know of a good command to do this, but here's a way to do it with a list comprehension:

l = [1,2,3,4,5,6,7]
n = 5
newlist = [l[i:i+n] for i in range(0,len(l),n)]

Edit: as a commenter pointed out, I had accidentally put l[i:i+n] in a list.

Upvotes: 1

Ofri Raviv
Ofri Raviv

Reputation: 24823

[a[n*k:n*(k+1)] for k in range(0,len(a)/n+1)]

Upvotes: 0

Greg Hewgill
Greg Hewgill

Reputation: 993901

You could do this:

[a[x:x+n] for x in range(0, len(a), n)]

(In Python 2, use xrange for efficiency; in Python 3 use range as above.)

Upvotes: 2

Related Questions