Сортировка и поиск с localeCompare


С задачей обычной сортировки, если речь про английский язык или числа, норм справляется sort() или sort((a, b) => a - b).

Но вот когда сортируются «неанглийские» строки, то тогда пригодится метод localeCompare у строк: sort((a, b) => a.localeCompare(b)), который сравнивает сроки учитывая действующую локаль пользовательской системы. Грубо говоря, где в текущей локали один Unicode-символ находится по сравнению с другим символом – перед ним или после него?

Что интересно, базовый метод появился в браузерах ещё практически с начала существования Chrome, Firefox и Safari. Но позже в него была добавлена поддержка дополнительных параметров, уточняющих как именно нужно сравнивать строки. Причём под капотом localeCompare будет вызывать Intl.Collator API, в браузерах его поддерживающих.

Настройка caseFirst — первыми идёт нижний регистр или верхний:

["Е", "а", "е", "А"]
.sort((a, b) => a.localeCompare(b, "ru"))
[
// по умолчанию сначала нижний регистр
// ["а","А","е","Е"]
("Е", "а", "е", "А")
].sort((a, b) => a.localeCompare(b, "ru", { caseFirst: "upper" }));
// теперь сначала идёт верхний регистр
// ["А","а","Е","е"]

Это актуально и для английских текстов тоже.

Настройка sensitivity — учитывать ли при сравнении регистр и вспомогательные символы вокруг буквы (a и á, е и ё):

"лёд".localeCompare("ЛЕД", "ru", { sensitivity: "variant" }) === 0;
// л ≠ д, е ≠ ё, л ≠ Л
// false
"лед".localeCompare("лёд", "ru", { sensitivity: "case" }) === 0;
// л ≠ д, е = ё, л ≠ Л.
// true
"Лёд".localeCompare("лёд", "ru", { sensitivity: "accent" }) === 0;
// л ≠ д, е ≠ ё, л = Л
// true
"лёд".localeCompare("ЛЕД", "ru", { sensitivity: "base" }) === 0;
// л ≠ д, е = ё, л = Л
// true

То есть если пользователь вводит в поиск ЛЕД, можно однозначно определить, что это лёд прямо в браузере.

Настройка numeric — для корректной сортировки строк с числами:

["09", "1", "2", "01", "10"]
.sort((a, b) => a.localeCompare(b))
[
// по умолчанию сортируется по символам
// ["01","09","1","10","2"]
("09", "1", "2", "01", "10")
].sort((a, b) => a.localeCompare(b, "ru", { numeric: true }));
// теперь строки учитываются как числа
// ["1","01","2","09","10"]

Так можно отсортировать строки с числами без явного приведения к числам.

Что радует, все возможные доп настройки доступны с Chrome 87, FF 85, Safari 14.1, Node 15.

Демо тут