2010-09-19 2 views
7

Je veux être en mesure de prendre une séquence comme:Est-ce la façon dont vous paginez ou y a-t-il un meilleur algorithme?

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

Utilisez une fonction comme:

my_paginated_sequence = get_rows(my_sequence, 3) 

Pour obtenir:

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

C'est ce que je suis venu avec par juste à y penser:

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 
+2

@Noon - thx, n'a pas pensé à ajouter cela. Aussi, es-tu un ninja par hasard? – orokusaki

+0

duplication possible de [Rend plusieurs objets à la fois à partir d'un objet itérable?] (Http://stackoverflow.com/questions/2202461/yield-multiple-objects-at-a-time-from-an-iterable-object) –

+0

duplication possible de [Comment divisez-vous une liste en morceaux de taille égale en Python?] (Http://stackoverflow.com/q/312443/54262) –

Répondre

11

Si vous savez que vous avez une séquence Découpable (liste ou tuple),

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

Cela est bien sûr un générateur, donc si vous avez absolument besoin d'une liste comme résultat, vous utiliserez list(getrows_byslice(seq, 3)) ou similaire, bien sûr.

Si ce que vous commencez avec un itérables générique, l'itertools recipes offre une aide à la recette grouper ...:

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) 

(encore une fois, vous aurez besoin d'appeler list sur ce si une liste est ce que vous voulez, bien sûr).

Étant donné que vous souhaitez réellement tronquer le dernier n-uplet au lieu de le remplir, vous devez "rogner" les valeurs de remplissage finales à partir du dernier tuple.

+0

voyez ce que je veux dire à propos de Jedi. J'ai l'impression que je ne serai jamais capable de faire des trucs comme ça directement dans mon cerveau. Avez-vous déjà ressenti cela dans la journée? – orokusaki

+0

@orokusaki, bien sûr - mais ensuite j'ai commencé à lire les docs (souvenez-vous que la fonction 'grouper' est citée directement dans les docs!). –

+1

En outre, je parle avec mon frère au téléphone de la façon dont vous êtes utile à la communauté Python dans son ensemble. Nous nous demandons tous les deux, qu'est-ce qui pousse votre enthousiasme à aider les autres?J'espère qu'un jour je pourrai être comme toi à propos de ce genre de choses, ou de ce que je ferai dans le futur. – orokusaki

0

Si vous êtes à la recherche pour la compréhension de la liste vers le haut, cela va faire le travail:

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']] 
6

Cette version fonctionne avec tout (peut-être paresseux et non Découpable) itérables et produit un paresseux itérables (autrement dit, il est un générateur et fonctionne avec tous les types de séquences, y compris d'autres générateurs):

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 

Quelques exemples:

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]] 
1

La fonction grouper dans les documents itertools est intelligent et concis; le seul problème est que vous pourriez avoir besoin de réduire les résultats, comme l'a souligné Alex Martelli. Je serais enclin à trouver une solution dans le sens de la réponse de Michał Marczyk, bien que je ne vois pas pourquoi cela ne pourrait pas être simplifié. Cela fonctionne pour tous les cas que je peux concevoir:

import itertools 

def paginate(seq, page_size): 
    i = iter(seq) 
    while True: 
     page = tuple(itertools.islice(i, 0, page_size)) 
     if len(page): 
      yield page 
     else: 
      return 
Questions connexes