summaryrefslogtreecommitdiffstats
path: root/askbot/utils/lists.py
blob: f69c8f203244bdd2333625aeab67264631c3e85a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
"""Utilities for working with lists and sequences."""

class LazyList(list):
    def __init__(self, get_data):
        self.data = get_data

def flatten(x):
    """
    Returns a single, flat list which contains all elements retrieved
    from the sequence and all recursively contained sub-sequences
    (iterables).

    Examples:
    >>> [1, 2, [3, 4], (5, 6)]
    [1, 2, [3, 4], (5, 6)]

    From http://kogs-www.informatik.uni-hamburg.de/~meine/python_tricks
    """
    result = []
    for el in x:
        if hasattr(el, '__iter__') and not isinstance(el, basestring):
            result.extend(flatten(el))
        else:
            result.append(el)
    return result

def batch_size(items, size):
    """
    Retrieves items in batches of the given size.

    >>> l = range(1, 11)
    >>> batch_size(l, 3)
    [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
    >>> batch_size(l, 5)
    [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]
    """
    return [items[i:i+size] for i in xrange(0, len(items), size)]

def batches(items, number):
    """
    Retrieves items in the given number of batches.

    >>> l = range(1, 11)
    >>> batches(l, 1)
    [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
    >>> batches(l, 2)
    [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]
    >>> batches(l, 3)
    [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]
    >>> batches(l, 4)
    [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
    >>> batches(l, 5)
    [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]

    Initial batches will contain as many items as possible in cases where
    there are not enough items to be distributed evenly.

    >>> batches(l, 6)
    [[1, 2], [3, 4], [5, 6], [7, 8], [9], [10]]
    >>> batches(l, 7)
    [[1, 2], [3, 4], [5, 6], [7], [8], [9], [10]]
    >>> batches(l, 8)
    [[1, 2], [3, 4], [5], [6], [7], [8], [9], [10]]
    >>> batches(l, 9)
    [[1, 2], [3], [4], [5], [6], [7], [8], [9], [10]]
    >>> batches(l, 10)
    [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]]

    If there are more batches than items, empty batches will be appended
    to the batch list.

    >>> batches(l, 11)
    [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10], []]
    >>> batches(l, 12)
    [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [], []]
    """
    div, mod= divmod(len(items), number)
    if div > 1:
        if mod:
            div += 1
        return batch_size(items, div)
    else:
        if not div:
            return [[item] for item in items] + [[]] * (number - mod)
        elif div == 1 and not mod:
            return [[item] for item in items]
        else:
            # mod now tells you how many lists of 2 you can fit in
            return ([items[i*2:(i*2)+2] for i in xrange(0, mod)] +
                    [[item] for item in items[mod*2:]])