0
|
1 """ |
|
2 (c) Smiley Chris 2007 |
|
3 |
|
4 This code comes from Django snippets ( http://djangosnippets.org/snippets/401/ ) |
|
5 According to the Terms of Service ( http://djangosnippets.org/about/tos/ ), the code can be freely used |
|
6 |
|
7 Template filters to partition lists into rows or columns. |
|
8 |
|
9 A common use-case is for splitting a list into a table with columns:: |
|
10 |
|
11 {% load partition %} |
|
12 <table> |
|
13 {% for row in mylist|columns:3 %} |
|
14 <tr> |
|
15 {% for item in row %} |
|
16 <td>{{ item }}</td> |
|
17 {% endfor %} |
|
18 </tr> |
|
19 {% endfor %} |
|
20 </table> |
|
21 """ |
|
22 |
|
23 from django.template import Library |
|
24 |
|
25 register = Library() |
|
26 |
|
27 def rows(thelist, n): |
|
28 """ |
|
29 Break a list into ``n`` rows, filling up each row to the maximum equal |
|
30 length possible. For example:: |
|
31 |
|
32 >>> l = range(10) |
|
33 |
|
34 >>> rows(l, 2) |
|
35 [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]] |
|
36 |
|
37 >>> rows(l, 3) |
|
38 [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9]] |
|
39 |
|
40 >>> rows(l, 4) |
|
41 [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] |
|
42 |
|
43 >>> rows(l, 5) |
|
44 [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]] |
|
45 |
|
46 >>> rows(l, 9) |
|
47 [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [], [], [], []] |
|
48 |
|
49 # This filter will always return `n` rows, even if some are empty: |
|
50 >>> rows(range(2), 3) |
|
51 [[0], [1], []] |
|
52 """ |
|
53 try: |
|
54 n = int(n) |
|
55 thelist = list(thelist) |
|
56 except (ValueError, TypeError): |
|
57 return [thelist] |
|
58 list_len = len(thelist) |
|
59 split = list_len // n |
|
60 |
|
61 if list_len % n != 0: |
|
62 split += 1 |
|
63 return [thelist[split*i:split*(i+1)] for i in range(n)] |
|
64 |
|
65 def rows_distributed(thelist, n): |
|
66 """ |
|
67 Break a list into ``n`` rows, distributing columns as evenly as possible |
|
68 across the rows. For example:: |
|
69 |
|
70 >>> l = range(10) |
|
71 |
|
72 >>> rows_distributed(l, 2) |
|
73 [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]] |
|
74 |
|
75 >>> rows_distributed(l, 3) |
|
76 [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]] |
|
77 |
|
78 >>> rows_distributed(l, 4) |
|
79 [[0, 1, 2], [3, 4, 5], [6, 7], [8, 9]] |
|
80 |
|
81 >>> rows_distributed(l, 5) |
|
82 [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]] |
|
83 |
|
84 >>> rows_distributed(l, 9) |
|
85 [[0, 1], [2], [3], [4], [5], [6], [7], [8], [9]] |
|
86 |
|
87 # This filter will always return `n` rows, even if some are empty: |
|
88 >>> rows(range(2), 3) |
|
89 [[0], [1], []] |
|
90 """ |
|
91 try: |
|
92 n = int(n) |
|
93 thelist = list(thelist) |
|
94 except (ValueError, TypeError): |
|
95 return [thelist] |
|
96 list_len = len(thelist) |
|
97 split = list_len // n |
|
98 |
|
99 remainder = list_len % n |
|
100 offset = 0 |
|
101 rows = [] |
|
102 for i in range(n): |
|
103 if remainder: |
|
104 start, end = (split+1)*i, (split+1)*(i+1) |
|
105 else: |
|
106 start, end = split*i+offset, split*(i+1)+offset |
|
107 rows.append(thelist[start:end]) |
|
108 if remainder: |
|
109 remainder -= 1 |
|
110 offset += 1 |
|
111 return rows |
|
112 |
|
113 def columns(thelist, n): |
|
114 """ |
|
115 Break a list into ``n`` columns, filling up each column to the maximum equal |
|
116 length possible. For example:: |
|
117 |
|
118 >>> from pprint import pprint |
|
119 >>> for i in range(7, 11): |
|
120 ... print '%sx%s:' % (i, 3) |
|
121 ... pprint(columns(range(i), 3), width=20) |
|
122 7x3: |
|
123 [[0, 3, 6], |
|
124 [1, 4], |
|
125 [2, 5]] |
|
126 8x3: |
|
127 [[0, 3, 6], |
|
128 [1, 4, 7], |
|
129 [2, 5]] |
|
130 9x3: |
|
131 [[0, 3, 6], |
|
132 [1, 4, 7], |
|
133 [2, 5, 8]] |
|
134 10x3: |
|
135 [[0, 4, 8], |
|
136 [1, 5, 9], |
|
137 [2, 6], |
|
138 [3, 7]] |
|
139 |
|
140 # Note that this filter does not guarantee that `n` columns will be |
|
141 # present: |
|
142 >>> pprint(columns(range(4), 3), width=10) |
|
143 [[0, 2], |
|
144 [1, 3]] |
|
145 """ |
|
146 try: |
|
147 n = int(n) |
|
148 thelist = list(thelist) |
|
149 except (ValueError, TypeError): |
|
150 return [thelist] |
|
151 list_len = len(thelist) |
|
152 split = list_len // n |
|
153 if list_len % n != 0: |
|
154 split += 1 |
|
155 return [thelist[i::split] for i in range(split)] |
|
156 |
|
157 register.filter(rows) |
|
158 register.filter(rows_distributed) |
|
159 register.filter(columns) |
|
160 |
|
161 def _test(): |
|
162 import doctest |
|
163 doctest.testmod() |
|
164 |
|
165 if __name__ == "__main__": |
|
166 _test() |