Ускоренная генерация таблиц содержимого в Django
Я пытаюсь сделать таблицу содержимого для моего queryset в Django следующим образом:
def get_toc(self):
toc = {}
qs = self.get_queryset()
idx = set()
for q in qs:
idx.add(q.title[0])
idx = list(idx)
idx.sort()
for i in idx:
toc[i] = []
for q in qs:
if q.title[0] == i:
toc[i].append(q)
return toc
Но это имеет временную сложность O(n^2). Есть ли лучший способ сделать это?
Я считаю, что это эквивалентно вышеописанному, но должно быть намного быстрее, так как использует движок базы данных для сортировки и де-дупликации:
def get_toc(self):
return {
q.title[0]: q
for q in self.get_queryset().distinct("title").order_by('title')
}
Вместо того чтобы перебирать все запросы для каждого элемента в idx, пройдитесь по qs в качестве крайнего и единственного цикла for, добавляя каждый запрос в соответствующий бакет. Примерно так:
def get_toc(self):
toc = {}
qs = self.get_queryset()
for q in qs:
t = q.title[0]
toc[t].setdefault(t, []).append(value)
return toc
Первоначальное решение было не таким плохим, как O(n^2) - это O(nm), где n - длина queryset, а m - количество уникальных названий. В худшем случае (когда каждая запись уникальна по названию) это превратилось бы в O(n^2). Новое решение - O(n).
Это похоже не на оглавление, а на глоссарий, где первый символ термина отображается на список терминов.
Мы можем работать с .groupby(…) [python-doc] здесь:
from itertools import groupby
result = {
k: list(vs)
for k, vs in groupby(
self.get_queryset().order_by('title'), lambda x: x.title[0]
)
}