Алгоритм JavaScript Функция "sort ()" - PullRequest
14 голосов
/ 06 августа 2010

Недавно, когда я работал с функцией JavaScript «sort ()», в одном из руководств я обнаружил, что эта функция неправильно сортирует числа. Вместо того, чтобы сортировать числа, необходимо добавить функцию, которая сравнивает числа, например, следующий код: -

<script type="text/javascript">
function sortNumber(a,b)
{
    return a - b;
}

var n = ["10", "5", "40", "25", "100", "1"];
document.write(n.sort(sortNumber));
</script>

Выходные данные тогда получаются как: -

1,5,10,25,40,100

Теперь я не понял, почему это происходит, и кто-нибудь может подробно рассказать, какой тип алгоритма используется в этой функции " sort () "? Это связано с тем, что для любого другого языка я не нашел такой проблемы, когда функция неправильно сортировала числа .

Любая помощь очень ценится.

Ответы [ 7 ]

26 голосов
/ 01 ноября 2012

Чтобы ответить на ваш вопрос о том, как работает функция сортировки, я объясню это подробно.Как было сказано в большинстве ответов здесь, вызов только sort() для массива отсортирует ваш массив, используя строки.Преобразование ваших целых чисел в строки, а также.Blech!

Если вы считаете свои предметы символами, а не числами, имеет смысл, что они будут отсортированы таким образом.Хороший способ убедиться в этом - назначить буквы вашим номерам.

//0 = a
//1 = b
//2 = c
//4 = e
//5 = f
//These two arrays are treated the same because they're composed of strings.
var nums  = ["10", "5", "40", "25", "100", "1"];
var chars = ["ba", "f", "ea", "cf", "baa", "b"];

//Here we can see that sort() correctly sorted these strings. Looking at the
//alphabetical characters we see that they are in the correct order. Looking
//at our numbers in the same light, it makes sense that they are sorted
//this way as well. After all, we did pass them as strings to our array.
chars.sort(); //["b", "ba", "baa", "cf", "ea", "f"]
nums.sort();  //["1", "10", "100", "25", "40", "5"]

//The bad part of sort() comes in when our array is actually made up of numbers.
var nums = [10, 5, 40, 25, 100, 1];
nums.sort(); //[1, 10, 100, 25, 40, 5]

//As a result of the default sorting function converting numbers to strings 
//before sorting, we get an unwanted result. We can fix this by passing in our 
//own function as a parameter to sort().

Вы можете управлять сортировкой массива, передавая свою собственную функцию в качестве параметра функции sort().Это хорошо, но если вы не знаете, как работает функция sort(), она действительно не принесет вам пользы.

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

Сортировка чисел

Давайте рассмотрим простой пример, и я проведу вас через него:

var arr = [50, 90, 1, 10, 2];

arr = arr.sort(function(current, next){
    //My comments get generated from here
    return current - next;
});

//1 : current = 50, next = 90
//  : current - next (50 - 90 = -40)
//  : Negative number means no re-arranging
//  : Array now looks like [50, 90, 1, 10, 2]
//
//2 : current = 90, next = 1
//  : current - next (90 - 1 = 89)
//  : Positive number means sort() will switch these positions in the array
//  : Array now looks like [50, 1, 90, 10, 2]
//
//If sort() didn't backtrack, the next check would be 90 and 10, switch those 
//positions, check 90 and 2, and switch again. Making the final array
//[50, 1, 10, 2, 90], not sorted. But lucky for us, sort() does backtrack.
//
//3 : current = 50, next = 1
//  : current - next (50 - 1 = 49)
//  : Positive number means sort() will switch these positions in the array
//  : Array now looks like [1, 50, 90, 10, 2]
//
//If sort() wasn't smart, it would now check 50 and 90 again. What a waste! 
//But lucky for us again, sort() is smart and knows it already made this 
//check and will continue on.
//
//4 : current = 90, next = 10
//  : current - next (90 - 10 = 80)
//  : Positive number means sort() will switch these positions in the array
//  : Array now looks like [1, 50, 10, 90, 2]
//
//sort() backtracks one position and sees that it has not checked 50 and 10
//
//5 : current = 50, next = 10
//  : current - next (50 - 10 = 40)
//  : Positive number means sort() will switch these positions in the array
//  : Array now looks like [1, 10, 50, 90, 2]
//
//sort() backtracks one position and sees that it has not checked 1 and 10
//
//6 : current = 1, next = 10
//  : current - next (1 - 10 = -9)
//  : Negative number means no re-arranging
//  : Array now looks like [1, 10, 50, 90, 2]
//
//sort() remembers that it already checked 10 and 50 so it skips ahead
//sort() remembers that it already checked 50 and 90 so it skips ahead
//
//7 : current = 90, next = 2
//  : current - next (90 - 2 = 88)
//  : Positive number means sort() will switch these positions in the array
//  : Array now looks like [1, 10, 50, 2, 90]
//
//sort() backtracks one position and sees that it has not checked 50 and 2
//
//8 : current = 50, next = 2
//  : current - next (50 - 2 = 48)
//  : Positive number means sort() will switch these positions in the array
//  : Array now looks like [1, 10, 2, 50, 90]
//
//sort() backtracks one position and sees that it has not checked 10 and 2
//
//9 : current = 10, next = 2
//  : current - next (10 - 2 = 8)
//  : Positive number means sort() will switch these positions in the array
//  : Array now looks like [1, 2, 10, 50, 90]
//
//sort() backtracks one position and sees that it has not checked 1 and 2
//
//10: current = 1, next = 2
//  : current - next (1 - 2 = -1)
//  : Negative number means no re-arranging
//  : Array now looks like [1, 2, 10, 50, 90]
//
//sort() remembers that it already checked 2 and 10 so it skips ahead
//sort() remembers that it already checked 10 and 50 so it skips ahead
//sort() remembers that it already checked 50 and 90 so it skips ahead
//sort() has no more items to check so it returns the final array
//which is [1, 2, 10, 50, 90]

Если вы хотите, чтобы массив упорядочивался в порядке убывания [90, 50, 10, 2, 1], вы можете просто изменить оператор возврата с return current - next; на return next - current; следующим образом:

var arr = [50, 90, 1, 10, 2];

arr = arr.sort(function(current, next){
    //My comments get generated from here
    return next - current;
});

//1 : current = 50, next = 90
//  : next - current (90 - 50 = 40)
//  : Positive number means sort() will switch these positions in the array
//  : Array now looks like [90, 50, 1, 10, 2]
//
//2 : current = 50, next = 1
//  : next - current (1 - 50 = -49)
//  : Negative number means no re-arranging
//  : Array now looks like [90, 50, 1, 10, 2]
//
//etc.

Не имеет значения, является ли ваш массивсостоит из «строковых чисел» "5" или просто чисел 5 при использовании собственной функции для сортировки чисел.Потому что, когда JavaScript выполняет математику, он рассматривает «строковые числа» как числа.т.е. "5" - "3" = 2

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

Когда вы сортируете строки, вы можете сравнивать их, используя операторы > и < (больше и меньше).Оператор «больше» сортирует строку по возрастанию (AZ, 1-9), а оператор «меньше» сортирует по убыванию (ZA, 9-1).В разных браузерах используются разные алгоритмы сортировки, поэтому при сортировке по строкам необходимо убедиться, что вы возвращаете либо 1, либо -1, а не true или false.

Например, это работает в Chrome и FF, но не в IE:

var arr = ['banana', 'orange', 'apple', 'grape'];

arr = arr.sort(function(current, next){
    return current > next;
});

Чтобы убедиться, что алгоритм сортировки работает в любом браузере, используйте троичный оператор.

var arr = ['banana', 'orange', 'apple', 'grape'];

arr = arr.sort(function(current, next){
    return current > next? 1: -1;
});

При изменении способа сортировки (в порядке возрастания или убывания)), помимо смены операторов, вы можете оставить тот же оператор и переключать переменные current и next, как мы это делали при сортировке чисел.Или, поскольку мы используем троичный оператор, вы можете переключать 1 и -1.

Сортировка объектов

Вот еще один изящный прием, который я решил добавить сюда.Вы можете сортировать объекты, если вы добавляете их в массив и используете их для сравнения.Вот пример.

var arr = [
    {id: 2, name: 'Paul'},
    {id: 1, name: 'Pete'}
];

//sort numerically
arr = arr.sort(function(current, next){
    return current.id - next.id;
});
//Array now looks like [{id: 1, name: 'Pete'}, {id: 2, name: 'Paul'}]

//sort alphabetically
arr = arr.sort(function(current, next){
    return current.name > next.name? 1: -1;
});
//Array now looks like [{id: 2, name: 'Paul'}, {id: 1, name: 'Pete'}]

Резюме

Сортировка чисел в по возрастанию (1, 2, 3 ...) : function(a, b){return a - b;}в в порядке убывания (9, 8, 7 ...) : function(a, b){return b - a;}

для сортировки строк в в порядке возрастания (A, B, C ...) : function(a, b){return a > b? 1: -1;}в в порядке убывания (Z, Y, X ...) : function(a, b){return b > a? 1: -1;}

Для сортировки объектов добавьте их в массив,затем сортируйте по ключу: function(a, b){return a.key - b.key;}

13 голосов
/ 06 августа 2010

Что ж, если вы сортируете следующий список, он содержит только строки:

var n = ["10", "5", "40", "25", "100", "1"];

Так что я бы ожидал, что любой язык будет сравнивать их как строки, что приведет к сортировкепорядок:

var n = ["1", "10", "100", "25", "40", "5"];

Что требует от вашего кода использования пользовательской сортировки (как вы это сделали) для приведения строк обратно к целым числам для целей сортировки.

Редактировать

Как уже упоминал Пойнти, по умолчанию метод JavaScript ( sort () ) сортирует элементы по алфавиту, включая числа:

По умолчанию sort ()Метод сортирует элементы по алфавиту и по возрастанию.Тем не менее, номера не будут отсортированы правильно (40 предшествует 5).Чтобы отсортировать числа, вы должны добавить функцию, которая сравнивает числа.

Просто потрясающе ... поэтому требуется специальная сортировка даже для массива целых чисел.

6 голосов
/ 06 августа 2010

Сортировка Javascript по умолчанию лексикографическая, алфавитная. Таким образом, насколько я понимаю, каждый элемент рассматривается как строка. Внутренний алгоритм сортировки - это, скорее всего, быстрая сортировка или слияние. Чтобы использовать быструю сортировку, нужно уметь связывать элементы друг с другом, больше, чем b? В случае строки это упорядочение уже реализовано.

Поскольку вы, возможно, захотите отсортировать свои собственные типы данных и т. Д., Вы можете предоставить функционал, определяющий порядок заказа двух элементов.

Из вашего примера ваш функционал определяет порядок двух чисел a и b. Затем сортировка Javascript использует вашу функцию, рассказывающую, как упорядочить элементы.

Оказывается, что Mergesort используется Mozilla, посмотрите: Реализация Javascript Array.sort?

5 голосов
/ 06 августа 2010

Функция sort отсортирует ваш массив в алфавитном порядке сортировки , даже если он состоит из целых чисел;Вот почему ваш массив сортируется так, вызывая sort без параметра.

sortOrder - это функция сравнения, которая используется для определения нового порядка сортировки для массива;эта функция вернет

  • 0, если a и b имеют одинаковое значение
  • значение > 0, если a имеет большее значениечем b
  • значение < 0, если a имеет меньшее значение, чем b

В JavaScript "1" - "2" вернет -1, чтоэто число, а не строка больше;используя функцию сравнения sortOrder в вашем массиве, состоящем из чисел, заключенных в строки , вы упорядочиваете массив в порядке сортировки чисел , в результате чего получается 1,5,10,25,40,100, ине в 1,10,100,25,40,5

5 голосов
/ 06 августа 2010

Проблема заключается в использовании строк для представления чисел, что, к сожалению, функция сортировки делает по умолчанию.Строки отсортированы по алфавиту.Функция сравнения в вашем коде просто заставляет строки оцениваться как числа.

Я бы посчитал очень плохой дизайн API, так как функция сортировки по умолчанию рассматривает элементы как строки, но это может быть необходимо, учитывая JavaScriptсистема свободного типа ..

2 голосов
/ 06 августа 2010

Вы можете делегировать сортировку своей собственной функции сортировки:

function sortnum(a,b) {
 return parseInt(a,10)-parseInt(b,10);
}
var n = ["10", "5", "40", "25", "100", "1"];
alert(n.sort(sortnum)); //=>["1", "5", "10", "25", "40", "100"]
0 голосов
/ 06 августа 2010

А что если ваш n определяется как:

var n = [10, 5, 40, 25, 100, 1]; 
...