views:

115

answers:

3

I want to be able to take a sequence like:

my_sequence = ['foo', 'bar', 'baz', 'spam', 'eggs', 'cheese', 'yogurt']

Use a function like:

my_paginated_sequence = get_rows(my_sequence, 3)

To get:

[['foo', 'bar', 'baz'], ['spam', 'eggs', 'cheese'], ['yogurt']]

This is what I came up with by just thinking through it:

def get_rows(sequence, num):
    count = 1
    rows = list()
    cols = list()
    for item in sequence:
        if count == num:
            cols.append(item)
            rows.append(cols)
            cols = list()
            count = 1
        else:
            cols.append(item)
            count += 1
    if count > 0:
        rows.append(cols)
    return rows

I ask questions like this because I always feel like there is some Jedi way of doing things that I just don't know about since I work by myself and never went to school, like:

x = LNode(format="paginator").initialize(3)

or something, you know what I mean?

A: 

If you are looking for straight up list comprehension, this will do the job:

L = ['foo', 'bar', 'baz', 'spam', 'eggs', 'cheese', 'yogurt']
[L[i*3 : (i*3)+3] for i in range((len(L)/3)+1) if L[i*3 : (i*3)+3]]
# [['foo', 'bar', 'baz'], ['spam', 'eggs', 'cheese'], ['yogurt']]
L = ['foo', 'bar', 'baz', 'spam', 'eggs', 'cheese']
# [['foo', 'bar', 'baz'], ['spam', 'eggs', 'cheese']]
inspectorG4dget
+2  A: 

If you know you have a sliceable sequence (list or tuple),

def getrows_byslice(seq, rowlen):
    for start in xrange(0, len(seq), rowlen):
        yield seq[start:start+rowlen]

This of course is a generator, so if you absolutely need a list as the result, you'll use list(getrows_byslice(seq, 3)) or the like, of course.

If what you start with is a generic iterable, the itertools recipes offer help with the grouper recipe...:

import itertools

def grouper(n, iterable, fillvalue=None):
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.izip_longest(fillvalue=fillvalue, *args)

(again, you'll need to call list on this if a list is what you want, of course).

Since you actually want the last tuple to be truncated rather than filled up, you'll need to "trim" the trailing fill-values from the very last tuple.

Alex Martelli
@Alex - see what I mean about Jedi. I feel like I'll never be able to do stuff like that straight out of my brain. Did you ever feel like that back in the day?
orokusaki
@orokusaki, of course -- but then I started reading the docs (remember that `grouper` function is quoted right out of the docs!-).
Alex Martelli
@Alex - Also, I'm talking with my brother on the phone about how helpful you are to the Python community as a whole. We both wonder, what is it that drives your enthusiasm to help others? I hope someday I can be like you about this stuff, or whatever I'm doing in the future.
orokusaki
@orokusaki, I just try to "pay forward" some of the huge benefits that Python (and more generically programming, and more generically yet computers;-) brought to my life; always glad to hear that some do appreciate my efforts and find them useful!
Alex Martelli
@Alex - well thanks. And, if you're not driving a jet helicopter to Google each day, you're certainly "paying forward" more than you ask for in return.
orokusaki
A: 

This version works with any (possibly lazy and non-sliceable) iterable and produces a lazy iterable (in other words, it's a generator and works with all types of sequences, including other generators):

import itertools

def paginate(iterable, page_size):
    while True:
        i1, i2 = itertools.tee(iterable)
        iterable, page = (itertools.islice(i1, page_size, None),
                list(itertools.islice(i2, page_size)))
        if len(page) == 0:
            break
        yield page

Some examples:

In [61]: list(paginate(my_sequence, 3))
Out[61]: [['foo', 'bar', 'baz'], ['spam', 'eggs', 'cheese'], ['yogurt']]

In [62]: list(paginate(xrange(10), 3))
Out[62]: [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
Michał Marczyk