Алгоритм JavaScript "sort()" Функция

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

<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()? Это потому, что для любого другого языка я не нашел эту проблему, когда функция не правильно сортировала числа.

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

Ответы

Ответ 1

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

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

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

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

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

Изменить

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

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

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

Ответ 2

Чтобы ответить на вопрос о том, как работает функция сортировки, я подробно объясню это. Как было сказано в большинстве ответов здесь, вызов только 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 от 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

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

При сортировке строк вы можете сравнить их с помощью операторов > и < (больше и меньше). Оператор большего размера сортирует строку по возрастанию (A-Z, 1-9), а оператор меньшего порядка сортирует по убыванию (Z-A, 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;}

Ответ 3

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

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

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

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

Ответ 4

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

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

Ответ 5

Функция 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

Ответ 6

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

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"]

Ответ 7

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

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