Задайте свой вопрос о JavaScript на нашем форуме

Сортировка массивов в JavaScript

Задача — разобраться как работает сортировка в JavaScript, насколько она производительна и что с её помощью можно делать.

Метод sort

В JavaScript можно сортировать массивы с помощью метода sort. В качестве единственного необязательного аргумента метод принимает функцию, определяющую правила сортировки. Если sort вызвали без аргументов, то сортировка осуществляется по возрастанию значений элементов. Пример:

// Массив со значениями различных типов
var arr = [0, true, 'Вася', 'Петя', 56, NaN, false, 13, 'Коля'];
// Сортируем
arr.sort();
// Вернет [0,13,56,NaN,false,true,Вася,Коля,Петя]

 

[]

Сортируется массив, в контексте которого вызван метод, так что если вы желаете сохранить исходное состояние — сделайте копию и сортируйте её.

Функции сортировки

Вы можете настраивать сортировку, передавая методу sort специальную функцию. В качестве аргументов функция принимает два значения массива, которые передаст метод sort. Вернуть функция может следующие значения (спасибо за уточнение пользователю alik):

  • положительное (1,2,10), если условие сортировки истинно;
  • отрицательное (-0.1, -3, -20), если условие сортировки ложно;
  • 0, если сравниваемые значения равны.

Пример:

var arr = [1,2,3,4,5,6,7,8,9,10];
// Функции сортировки
function sIncrease(i, ii) { // По возрастанию
    if (i > ii)
        return 1;
    else if (i < ii)
        return -1;
    else
        return 0;
}
function sDecrease(i, ii) { // По убыванию
    if (i > ii)
        return -1;
    else if (i < ii)
        return 1;
    else
        return 0;
}
function sRand() { // Случайная
    return Math.random() > 0.5 ? 1 : -1;
}
arr.sort(sIncrease); // Вернет [1,2,3,4,5,6,7,8,9,10]
arr.sort(sDecrease); // Вернет [10,9,8,7,6,5,4,3,2,1]
arr.sort(sRand); // Вернет случайно отсортированный массив, например [1,10,3,4,8,6,9,2,7,5]
[]

Производительность

На первый взгляд может показаться, что сортировка в JavaScript непроизводительна, и в web-приложениях лучше сортировать данные на server-side. Небольшой эксперимент это опровергает. Будьте осторожны, в циклах на 100.000 элементов браузер может зависнуть!

Сортируем массивы c целочисленными значениями различной длины:

Нет данных

можете зависнуть

Автор, в ходе своих экспериментов на своем PC (Vista, Pentium Dual 2.2Ггц, 2Гб RAM) получил следующие результаты:

1000 10.000 100.000
IE 7 20-50 ms 200-650 ms завис
FF 3 1-2 ms 12-30 ms 150-400 ms
Safari 3 2-30 ms * 30-400 ms * 350-5000 ms *
Opera 9.5 2-5 ms 40-75 ms 500-1000 ms
Chrome 1.0 1-2 ms 10-30 ms 100-300ms

* В Safari случайная сортировка занимала ровно на порядок больше времени, чем сортировка по возрастанию/убыванию.

За исключением Internet Explorer, который не смог переварить массив на 100.000 элементов, сортировка показала себя вполне производительной операцией.

Многомерный массив

При необходимости можно усложнить функции сортировки. Например, чтобы сортировать многомерные массивы (массивы массивов), или массивы объектов, или любой другой массив с нетривиальными значениями. Пример:

var multi = [
    ['Nikolay', 34],
    ['Andrey', 23],
    ['Stanislav', 20]
];
// Функции сортировки
function sName(i, ii) { // По имени (возрастание)
    if (i[0] > ii[0])
        return 1;
    else if (i[0] < ii[0])
        return -1;
    else
        return 0;
}
function sAge(i, ii) { // По возрасту (возрастание)
    if (i[1] > ii[1])
        return 1;
    else if (i[1] < ii[1])
        return -1;
    else
        return 0;
}
multi.sort(sName); // Вернет [[Andrey,23],[Nikolay,34],[Stanislav,20]]
multi.sort(sAge); // Вернет  [[Stanislav,20],[Andrey,23],[Nikolay,34]]
[]

Отдельная страница со всеми примерами.

Итого мы

  • Разобрали как работает сортировка в JavaScript.
  • Научились писать функции сортировки.
  • Осознали что сортировка достаточно производительна, чтобы использовать её в своих приложениях на стороне клиента, снизив нагрузку на сервер.

Если вам есть что спросить или добавить — прошу в комменты.

Александр Бурцев 18 февраля 2009

© Все права на данную статью принадлежат порталу fastcoder.org. Перепечатка в интернет-изданиях разрешается только с указанием автора и прямой ссылки на оригинальную статью. Перепечатка в печатных изданиях допускается только с разрешения редакции.

Комментарии

rgbeast 18 февраля 2009, 17:20 #
Интересно по какому алгоритму работает sort. Странно, что функция может быть любой, даже рандомной, так как по хорошему функция должна быть транзитивна. Если a>b и b>c, то должно быть a>c. Если это неверно, то результат непредсказуем. В примере выше, если нажать сначала на сортировку по возрастанию, а затем на сортировку по рандому, видно, что единичка гораздо чаще попадает в первую половину списка, чем во вторую. Значит сортировка не вполне рандомная.
 
Bur 18 февраля 2009, 17:33 #
Откровенно говоря, способ рандомной сортировки был придуман мной случайно и после экспериментов принят как работающий. Потому и решил им поделится с читателями, т.к. встроенных методов в JavaScript нет.
Насчет алгоритма ничего конкретного сказать не могу, необходимо читать документацию.
Вполне возможно что различные производители браузеров используют разные алгоритмы, на это косвенно указывает увеличенное на порядок время случайной сортировки в Safari.
 
shcoder 19 февраля 2009, 22:57 #
Вероятнее всего это сортировка Хоара, так же известная как "Быстрая сортировка" (quicksort). Она основана на рекурсивном уменьшении числа инверсий в рассматриваемом массиве. Доказано, что в общем она наиболее производительна. А учитывая, что в большинстве сколь-нибудь серьёзных библиотек и фреймворков под функцией sort скрывается именно сортировка по методу Хоара, логично предположить, что и JavaScript - не исключение. Впрочем, конкретный ответ может зависеть от браузера.
Более подробно о сортировке Хоара в Википедии:
http://ru.wikipedia.org/wiki/%D0%91%D1%8B%D1%81%D1%82%D1%80%D0%B0%D1%8F_%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0

rgbeast
Странно, что функция может быть любой, даже рандомной, так как по хорошему функция должна быть транзитивна. Если a>b и b>c, то должно быть a>c. Если это неверно, то результат непредсказуем.

Именно так, результат и получается непредсказуемым, как и требовалось. Именно за счёт этого, кстати, происходит ощутимый рост времени выполнения сортировки. Транзитивность, как видно, не является требованием функции сравнения для сортировки, хотя, разумеется, необходима для непротиворечивой модели (и получения реального результата).
 
rgbeast 19 февраля 2009, 23:12 #
Требуется не просто непредсказуемый результат, а рандомный. В случае рандомный результата, я ожидаю, что число 1 попадет на 1ую,2ую 3ю, 10ую позиции с одинаковой вероятностью. Простой тест в мозилле показывает, что это не так. Таким образом, если мы выбираем 3 случайных статьи, то некоторые будут попадать в выборку чаще других. Такой рандом непригоден для большинства целей.
 
Bur 19 февраля 2009, 23:27 #
Я бы не стал принимать за истину, что еденица оказывается преимущественно в начале массива. По своим тестам в FF такого не заметил. Для верности набросаю скрипт для подсчета статистики позиций еденицы. О результатах сообщу.
 
rgbeast 19 февраля 2009, 23:37 #
Нужно тест, который:
1. проверяет равномерность распределения каждого элемента
2. отсутствие корреляции в расположении элементов (расстояние между соседними элементами должно стать случайным после сортировки).

И так, чтобы можно было проверить в разных браузерах.
 
rgbeast 19 февраля 2009, 23:38 #
Есть способ проще. Сгенерить рандомное число в пару к каждому элементу массива. И отортировать по этому числу. Не будет никаких проблем.
 
Bur 19 февраля 2009, 23:23 #
Возможно мы имеем дело с одной из реализаций быстрой сортировки Хоара. Алгоритм должен иметь конечное число итераций, иначе compare-функция с рандомной выдачей приводила бы к бесконечной рекурсии. Что подверждают тесты.
По вашей ссылке в Википедии есть реализация алгоритма на JavaScript. Такой вызов qsort приводит к бесконечной рекурсии:
qsort([1,4,6,3,5,6,8,6,3], function(a, b) {return Math.round() ? -1 : 1;});
 
Kolyaj 17 июня 2009, 17:10 #
Вы забыли один важный нюанс: сортировка по-умолчанию лексикографическая. Отсортируйте, например, массив [1, 2, 10, 20].

В Chrome, кстати, нативная сортировка реализована на JavaScript (как и некоторые другие функции), поэтому код можно подсмотреть, вставив в адресную строку javascript: alert([].sort.toString()); void(0);
 
Bur 17 июня 2009, 17:21 #
Про возможность посмотреть нативный код сортировки в Google Chrome не знал, спасибо! Поизучаю на досуге их код, как и реализацию других методов...

Про лексикографическую сортировку знаю, получим [1,10,2,20]. Пожалуй стоит упомянуть в статье.
 
 
Rambler's Top100 Flede HTML valid CSS valid