400x Сортировка ускорения путем переключения a.localeCompare(b) на (a <b? -1: (a> b? 1: 0))
Переключение функции сортировки javascript из
myArray.sort(function (a, b) {
return a.name.localeCompare(b.name);
});
к
myArray.sort(function (a, b) {
return (a.name < b.name ? -1 : (a.name > b.name ? 1 : 0));
});
Мне удалось сократить время сортировки массива элементов размером ~ 1700 в Chrome с 1993 по миллисекунды до 5 миллисекунд. Почти 400-кратное ускорение. К сожалению, это происходит за счет правильной сортировки неанглийских строк.
Очевидно, я не могу заблокировать свой пользовательский интерфейс в течение 2 секунд, когда я попытаюсь выполнить сортировку. Есть ли что-нибудь, что я могу сделать, чтобы избежать ужасно медленного localeCompare, но все же поддерживать поддержку локализованных строк?
Ответы
Ответ 1
Значительное улучшение производительности можно получить, предварительно объявив объект-сборщик и используя его метод сравнения. EG:
const collator = new Intl.Collator('en', { numeric: true, sensitivity: 'base' });
arrayOfObjects.sort((a, b) => {
return collator.compare(a.name, b.name);
});
Вот эталонный скрипт, сравнивающий 3 метода:
const arr = [];
for (let i = 0; i < 2000; i++) {
arr.push('test-${Math.random()}');
}
const arr1 = arr.slice();
const arr2 = arr.slice();
const arr3 = arr.slice();
console.time('#1 - localeCompare');
arr1.sort((a, b) => a.localeCompare(
b,
undefined, {
numeric: true,
sensitivity: 'base'
}
));
console.timeEnd('#1 - localeCompare');
console.time('#2 - collator');
const collator = new Intl.Collator('en', {
numeric: true,
sensitivity: 'base'
});
arr2.sort((a, b) => collator.compare(a, b));
console.timeEnd('#2 - collator');
console.time('#3 - non-locale');
arr3.sort((a, b) => (a < b ? -1 : (a > b ? 1 : 0)));
console.timeEnd('#3 - non-locale');
Ответ 2
Эффективный подход, который я нашел при работе с/в основном/латинскими символами, заключается в использовании оператора всякий раз, когда обе строки соответствуют определенному регулярному выражению. EG: /^[\w-.\s,]*$/
Это намного быстрее, если обе строки соответствуют выражению, и в худшем случае это выглядит немного медленнее, чем слепой вызов localeCompare.
Пример здесь: http://jsperf.com/operator-vs-localecompage/11
Обновление: кажется, что Intl.Collator в настоящее время является лучшим вариантом для повышения производительности из всех возможных: https://jsperf.com/operator-vs-localecompage/22
Ответ 3
Трудно узнать самый быстрый вид, не видя данные, которые вы сортируете. Но у jsperf есть много хороших тестов, показывающих различия в производительности между типами сортировки:
http://jsperf.com/javascript-sort/45
http://jsperf.com/sort-algorithms/31
Однако ни одна из них не учитывает локализованные строки, и я бы предположил, что нет простого способа сортировать локализованные строки, и localeCompare, вероятно, является лучшим решением для этого.
В обзоре mozilla говорится:
"При сравнении большого количества строк, например при сортировке больших массивов, лучше создать объект Intl.Collator и использовать функцию, предоставляемую свойством сравнения".
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare
Но, перейдя к ссылке Intl.Collator, она показывает, что это не поддержка firefox/safari
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator
вы можете попробовать использовать некоторые параметры на localCompare, чтобы ускорить работу. Но я только что сделал быстрый тест, изменивший уровень чувствительности, и похоже, что он не улучшит производительность:
list.sort(function(a, b) {
return a.localeCompare(b, {sensitivity:'base'});
});
http://jsperf.com/sort-locale-strings
Ответ 4
Попробуйте отсортировать его в 2 этапа:
- С оператором: как вы сказали, он будет в 400 раз быстрее
- Затем с
localCompare()
: теперь это меньше сравнений, потому что массив в основном отсортирован.
Примечание. Я думаю, что localCompare()
будет вызван в основном по крайней мере с одной строкой, которая не является английской. Поэтому количество вызовов localCompare()
с 2 английскими строками должно быть значительно уменьшено.
Вот код:
myArray.sort(function(a, b) {
return (a.name < b.name ? -1 : (a.name > b.name ? 1 : 0));
});
myArray.sort(function(a, b) {
return a.name.localeCompare(b.name);
});
Это решение имеет то преимущество, что оно является коротким и простым в использовании. Это будет эффективно, если массив содержит в основном английские строки. Чем больше не английских строк, тем менее полезным будет первый сорт. Но, как легко добавить в свои скрипты, также легко увидеть, стоит ли этот подход.
Теперь, если бы я был вами, я также использовал бы Intl.Collator
, поскольку он называется намного быстрее, чем localCompare()
, когда у вас есть много сравнений.
Ответ 5
Я не знаю, что вы все еще ищете решение этой проблемы
// Defaulted to ascending
// 1 asc | -1 desc
var direction = 1;
myArray.sort(function (a, b) {
return a.name.localeCompare(b.name) === 1 ? direction : -1 * direction;
});
я добавил проверку === 1
на ваш код, и это улучшило perf 400x, что означает, что оба имеют сопоставимые первичные номера.
Числа Perf с localeCompare arr size: 3200
Среднее время прошло 10 повторений: 60 мс
Числа Perf s > подход. Время avg заняло 55 мс