Раскрываем тайну JavaScript sort()

Раскрываем тайну JavaScript sort()

От автора: в JavaScript есть метод sort(), который вы можете использовать в массивах. Но результаты почти всегда странные и не дают того, что вы изначально ожидали.

Так, например, если у вас есть следующий массив [9, 8, 12, 1, 33, 21], sort() вернет [1, 12, 21, 33, 8, 9]. На первый взгляд, это не имеет смысла, и это потому, что метод JavaScript sort() — не совсем то, что вы думаете. Возвращенный массив почти не сортируется — или, может быть, сортируется, но не так, как мы хотим.

Это потому что метод JavaScript sort() преобразует каждый элемент в массиве в строку и создает последовательность, сравнивая каждый элемент массива на основе значений кода UTF-16, когда обратный вызов не указан.

UTF что? Я знаю. Добро пожаловать в странные, но очень логичные части JavaScript.

Как работает UTF-16

JavaScript не является типизированным языком — настолько, насколько многие из нас хотят. Когда используется sort(), он автоматически вызывает метод String() для каждого элемента массива по умолчанию и преобразует все в строки. Это делается для того, чтобы гарантировать, что вещи могут быть отсортированы универсальным образом. Проще сортировать вещи, когда они одного типа.

UTF-16 расшифровывается как 16-битный формат преобразования Unicode. Это стандартизированная форма для перевода битов в понятный человеку формат. Это в основном таблица перевода для соответствующего символа.

Когда вы используете JavaScript sort(), вы по существу сортируете строки в соответствие с порядком символов в этой таблице. Вот почему 33 в отсортированном массиве выше, чем 8.

Это потому, что символ 3 расположен в таблице выше 8. Сортировка выполняется на основе символов, а не математических правил. Когда применяется эта логика, метод JavaScript sort() не ошибается — он просто неправильно понимается.

Как превратить числа в числа

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

Эта функция обратного вызова принимает 2 аргумента — a и b — для целей соглашения. Эти аргументы используются для создания уравнения, которое возвращает 1, -1 или 0.

Когда уравнение возвращает 1, то можно продолжать a. Если уравнение возвращает -1, то можно продолжить b. Если уравнение равно 0, то это означает, что оба значения равны, и это конец алгоритма рекурсивной сортировки sort().

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

function sortNumbers(a, b) { if (a > b) { return 1; } else if (b > a) { return -1; } else { return 0; }
}

Это наша функция обратного вызова, которая поможет отсортировать числа в правильном и возрастающем порядке. Поэтому, когда вы хотите отсортировать массив чисел, вы можете сделать это следующим образом: arrayNameHere.sort(sortNumbers);

Чтобы инвертировать порядок, вы можете просто инвертировать логику кода для b > a и b < a.

Как насчет (a, b) => a- b?

Во многих руководствах и ответах на StackOverflow вы, возможно, видели решения, которые выглядят примерно так: arrayNameHere.sort((a, b) => a — b);

Что касается 1 и -1, они не обязательно должны быть этими двумя числами, они просто должны попасть в один из двух диапазонов либо положительных, либо отрицательных чисел. sort() будет по-прежнему обрабатывать это так же, как в примере кода выше.

0 — это определенное число, которое обозначает конец рекурсии сортировки, так что это единственное не обращаемое значение.

Вот почему (a, b) => a — b работает. Например, если a = 5 и b = 2, a — b дает положительное число, значит a больше, что возвращает эквивалент 1.

Следует отметить, что этот вид логики сортировки работает только с числовыми типами или объектами, которые возвращают числовые значения при использовании valueOf(). Таким образом, алгоритмически для массива с 3 значениями это выглядит примерно так.

А как насчет нечисловой сортировки, например месяцев?

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

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

var someItems = ["Feb", "Jan", "Apr", "Dec", "Oct"]; function sortMonths(a, b){ var correctMonthsOrder = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; return correctMonthsOrder.indexOf(a) - correctMonthsOrder.indexOf(b);
} someItems.sort(sortMonths);

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

Заключение

Я надеюсь, что эта статья прояснила некоторые загадки изящного метода JavaScript sort().

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

Представьте, что вам нужно писать код внутри sortMonths снова и снова в нескольких местах. Это может стать довольно громоздко. Или, если логика усложняется, и вы начинаете терять скобки в процессе. (a, b) => a — b может показаться коротким выражением в краткосрочной перспективе, но когда логика расширяется, все может очень быстро визуально запутаться. Это всего лишь прием, который поможет сделать ваш код более поддерживаемым.

Это в основном вкратце все, что касается работы sort(). Спасибо за чтение.

Автор: Aphinya Dechalert

Источник: https://medium.com

Редакция: Команда webformyself.