Coverage for website/newsletters/templatetags/listutil.py: 84.38%
52 statements
« prev ^ index » next coverage.py v7.6.7, created at 2025-08-14 10:31 +0000
« prev ^ index » next coverage.py v7.6.7, created at 2025-08-14 10:31 +0000
1"""Template filters to partition lists into rows or columns.
3From https://djangosnippets.org/snippets/401/
5A common use-case is for splitting a list into a table with columns::
7 {% load partition %}
8 <table>
9 {% for row in mylist|columns:3 %}
10 <tr>
11 {% for item in row %}
12 <td>{{ item }}</td>
13 {% endfor %}
14 </tr>
15 {% endfor %}
16 </table>
17"""
19from django.template import Library
21register = Library()
24def rows(thelist, n):
25 """Break a list into ``n`` rows, filling up each row to the maximum equal length possible.
27 For example::
29 >>> l = range(10)
31 >>> rows(l, 2)
32 [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]
34 >>> rows(l, 3)
35 [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9]]
37 >>> rows(l, 4)
38 [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
40 >>> rows(l, 5)
41 [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]]
43 >>> rows(l, 9)
44 [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [], [], [], []]
46 # This filter will always return `n` rows, even if some are empty:
47 >>> rows(range(2), 3)
48 [[0], [1], []]
49 """
50 try:
51 n = int(n)
52 thelist = list(thelist)
53 except (ValueError, TypeError):
54 return [thelist]
55 list_len = len(thelist)
56 split = list_len // n
58 if list_len % n != 0:
59 split += 1
60 return [thelist[split * i : split * (i + 1)] for i in range(n)]
63def rows_distributed(thelist, n):
64 """Break a list into ``n`` rows, distributing columns as evenly as possible across the rows.
66 For example::
68 >>> l = range(10)
70 >>> rows_distributed(l, 2)
71 [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]
73 >>> rows_distributed(l, 3)
74 [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]
76 >>> rows_distributed(l, 4)
77 [[0, 1, 2], [3, 4, 5], [6, 7], [8, 9]]
79 >>> rows_distributed(l, 5)
80 [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]]
82 >>> rows_distributed(l, 9)
83 [[0, 1], [2], [3], [4], [5], [6], [7], [8], [9]]
85 # This filter will always return `n` rows, even if some are empty:
86 >>> rows_distributed(range(2), 3)
87 [[0], [1], []]
88 """
89 try:
90 n = int(n)
91 thelist = list(thelist)
92 except (ValueError, TypeError):
93 return [thelist]
94 list_len = len(thelist)
95 split = list_len // n
97 remainder = list_len % n
98 offset = 0
99 r = []
100 for i in range(n):
101 if remainder:
102 start, end = (split + 1) * i, (split + 1) * (i + 1)
103 else:
104 start, end = split * i + offset, split * (i + 1) + offset
105 r.append(thelist[start:end])
106 if remainder:
107 remainder -= 1
108 offset += 1
109 return r
112def columns(thelist, n):
113 """Break a list into ``n`` columns, filling up each column to the maximum equal length possible.
115 For example:
117 >>> from pprint import pprint
118 >>> for i in range(7, 11):
119 ... print('%sx%s:' % (i, 3))
120 ... pprint(columns(range(i), 3), width=20)
121 7x3:
122 [[0, 3, 6],
123 [1, 4],
124 [2, 5]]
125 8x3:
126 [[0, 3, 6],
127 [1, 4, 7],
128 [2, 5]]
129 9x3:
130 [[0, 3, 6],
131 [1, 4, 7],
132 [2, 5, 8]]
133 10x3:
134 [[0, 4, 8],
135 [1, 5, 9],
136 [2, 6],
137 [3, 7]]
139 # Note that this filter does not guarantee that `n` columns will be
140 # present:
141 >>> pprint(columns(range(4), 3), width=10)
142 [[0, 2],
143 [1, 3]]
144 """
145 try:
146 n = int(n)
147 thelist = list(thelist)
148 except (ValueError, TypeError):
149 return [thelist]
150 list_len = len(thelist)
151 split = list_len // n
152 if list_len % n != 0:
153 split += 1
154 return [thelist[i::split] for i in range(split)]
157register.filter(rows)
158register.filter(rows_distributed)
159register.filter(columns)
162def _test():
163 import doctest
165 doctest.testmod()
168if __name__ == "__main__": 168 ↛ 169line 168 didn't jump to line 169 because the condition on line 168 was never true
169 _test()