В Awesomplete сортировка элементов списка отделена от фильтрации, что позволяет гибко управлять порядком отображения подсказок после применения поискового запроса. Библиотека использует комбинацию двух механизмов: filter (отбор элементов) и sort (упорядочивание уже отобранных элементов).
По умолчанию используется простая стратегия, при которой совпадения упорядочиваются по степени релевантности, зависящей от позиции вхождения подстроки. Однако встроенное поведение не покрывает всех сценариев, особенно когда требуется бизнес-логика ранжирования.
Сортировка в Awesomplete реализуется как функция сравнения двух элементов:
sort: function(a, b, input) {
return a.localeCompare(b);
}
Функция получает:
a — первый элемент спискаb — второй элемент спискаinput — текущее значение поля вводаРезультат:
a выше bb выше aДля кастомного поведения сортировка передаётся через опции конструктора:
new Awesomplete(input, {
list: ["Apple", "Apricot", "Banana", "Blueberry"],
sort: function(a, b, input) {
return a.length - b.length;
}
});
В этом примере приоритет получают более короткие строки, независимо от совпадения с вводом.
Часто требуется поднимать элементы, которые начинаются с введённого текста:
function prefixPrioritySort(a, b, input) {
const ia = a.toLowerCase().indexOf(input.toLowerCase());
const ib = b.toLowerCase().indexOf(input.toLowerCase());
const aIsPrefix = ia === 0;
const bIsPrefix = ib === 0;
if (aIsPrefix && !bIsPrefix) return -1;
if (!aIsPrefix && bIsPrefix) return 1;
return a.localeCompare(b);
}
Логика:
Более сложная стратегия включает вычисление «веса совпадения». Например:
function relevanceSort(a, b, input) {
input = input.toLowerCase();
function score(str) {
str = str.toLowerCase();
if (str === input) return 0;
if (str.startsWith(input)) return 1;
if (str.includes(input)) return 2;
return 3;
}
const diff = score(a) - score(b);
if (diff !== 0) return diff;
return a.length - b.length;
}
Такая сортировка вводит уровни приоритета:
При работе с многоязычными списками важно использовать
localeCompare с указанием локали:
function localeSort(a, b) {
return a.localeCompare(b, 'ru', {
sensitivity: 'base',
ignorePunctuation: true
});
}
Параметры:
sensitivity: 'base' — игнорирование регистра и
диакритикиignorePunctuation — игнорирование знаков
пунктуацииЭто позволяет корректно обрабатывать кириллицу и латиницу в одном списке.
Иногда требуется сохранить порядок исходного массива при равных условиях:
function stableSortFactory(list) {
const indexMap = new Map(list.map((v, i) => [v, i]));
return function(a, b, input) {
const diff = a.localeCompare(b);
if (diff !== 0) return diff;
return indexMap.get(a) - indexMap.get(b);
};
}
Такая стратегия предотвращает «прыгающий» порядок элементов при одинаковом ранге.
Распространённый подход — использование нескольких факторов одновременно:
function weightedSort(a, b, input) {
input = input.toLowerCase();
function weight(str) {
str = str.toLowerCase();
let w = 0;
if (str.startsWith(input)) w += 100;
if (str.includes(input)) w += 50;
if (str === input) w += 200;
w += Math.max(0, 30 - str.length);
return w;
}
return weight(b) - weight(a);
}
Здесь итоговый порядок определяется суммой факторов:
Сортировка работает после фильтрации, поэтому её поведение зависит от выбранного фильтра:
Awesomplete.FILTER_CONTAINS = function(text, input) {
return text.toLowerCase().includes(input.toLowerCase());
};
При такой фильтрации сортировка получает уже сокращённый набор данных, что делает ранжирование более значимым, чем отбор.
Изменение сортировки напрямую влияет на восприятие интерфейса:
Awesomplete поддерживает не только строки, но и объекты:
new Awesomplete(input, {
list: [
{ label: "Apple", value: "apple" },
{ label: "Apricot", value: "apricot" },
{ label: "Banana", value: "banana" }
],
sort: function(a, b, input) {
return a.label.localeCompare(b.label);
}
});
В этом случае сортировка должна работать по полю label,
иначе сравнение будет некорректным.
Сортировка может динамически зависеть от длины введённого текста:
function adaptiveSort(a, b, input) {
if (input.length < 2) {
return a.localeCompare(b);
}
const ia = a.toLowerCase().indexOf(input.toLowerCase());
const ib = b.toLowerCase().indexOf(input.toLowerCase());
if (ia !== ib) return ia - ib;
return a.length - b.length;
}
Такой подход снижает агрессивность ранжирования при коротком вводе и усиливает его при уточнении запроса.
Для более устойчивого сравнения используется нормализация строк:
function normalize(str) {
return str
.toLowerCase()
.normalize("NFD")
.replace(/\p{Diacritic}/gu, "");
}
function exactPrioritySort(a, b, input) {
const nInput = normalize(input);
const na = normalize(a);
const nb = normalize(b);
if (na === nInput && nb !== nInput) return -1;
if (nb === nInput && na !== nInput) return 1;
return na.localeCompare(nb);
}
Нормализация устраняет различия в акцентах и регистрах, делая сортировку предсказуемой.
Несколько стратегий можно объединять через композицию:
function composeSort(...fns) {
return function(a, b, input) {
for (const fn of fns) {
const res = fn(a, b, input);
if (res !== 0) return res;
}
return 0;
};
}
const sort = composeSort(
exactPrioritySort,
prefixPrioritySort,
localeSort
);
Такой подход позволяет строить многоуровневую систему приоритетов без усложнения одной функции.