, чтобы сохранить свой прогресс
30 нояб. 2021 г., 22:26

Обсуждение задания "Where do I Belong"

Обьясните, как работают эти '(a, b) => a - b' костыли для функции sort()? И, есть ли нормальная, полноценная функция для сортировки по вознастанию?
Сергей, '(a, b) => a - b' - это не костыль для sort(), это просто один из вариантов функции, используемой для сортировки. И этот вариант даже представлен в документации MDN на странице Array.prototype.sort().
Но давайте все-таки разберемся, потому что тема действительно малопонятная.

Для начала сразу определимся, что '(a, b) => a - b' - это стрелочная функция, которая может быть передана в sort() как параметр и используется для того, чтобы задать пользовательский сценарий сортировки. Зачем это может быть нужно?
1. По умолчанию (если не задать свой собственный обработчик), сравнение элементов массива происходит в строковом формате. То есть даже если массив у нас содержит только цифры, отсортированы они будут как строки. Пример:
[1, 2, 10, 21].sort() // [1, 10, 2, 21]
Как видим, в результате 10 типа меньше, чем 2, но это все потому что сравниваются строки, а если рассматривать первые символы строк '10' и '2', то '1' меньше, чем '2'.

Вот в таком случае, нам нужно передать функцию-сортировщик, которая будет сравнивать именно как цифры. В нашем случае пример такой:
[1, 2, 10, 21].sort((a, b) => (a < b ? -1 : a > b ? 1: 0)) // [1, 2, 10, 21]
То есть здесь мы уже более четкие условия задаем: Если a меньше b, то возвращаем -1, если a больше b, то возвращаем +1, иначе возвращаем 0. Таким образом и определяется сортировка: поставить элемент перед, после, или оставить на месте. Но при этом не обязательно возвращать именно -1, +1 или 0, достаточно меньше нуля, больше нуля или равно нулю. В этом как раз и кроется смысл сокращенной формы a - b. Ведь при вычетании b из a, результат как раз и получится положительный, отрицательный или нулевой.
[1, 2, 10, 21].sort((a, b) => a - b) // [1, 2, 10, 21]
2. Если у нас более сложная структура элементов. К примеру, это объекты и мы хотим отсортировать их по какому-то полю. Например:
[{age: 18}, {age: 16}, {age: 25}].sort((a, b) => a.age - b.age) /* 0: {age: 16} 1: {age: 18} 2: {age: 25} */
Уточнение: данная функция-сортировщик вызывается не один раз, а для каждой пары перечисляемых элементов массива, пока массив не будет отсортирован полностью. К примеру, возьмем простой массив с уникальными числами:
[1,2,3].sort((a,b) => { console.log(a,b); return a - b; }) /* 2 1 3 2 */
Как видим, элемента 3, а функция сортировки была вызвана два раза (потому что 1 и 2 элементы, потом 2 и 3, и все они были однозначно отсортированы больше/меньше).
А вот посмотрим теперь такой пример:
[1,2,1].sort((a,b) => { console.log(a,b); return a - b; }) /* 2 1 1 2 1 2 1 1 */
Вот здесь уже 4 вызова, хотя элементов в массиве все так же 3. Это потому что в отдельные итерации результат разницы был нулевой и элементы оставались на своих местах, но потом их надо было сравнить с другими элементами.

Это несколько запутанно, но стоит все-таки просто уяснить этот момент с множественным вызовом функции-параметра. Такое много где используется, например в функциях Array.filter(), Array.map(), Array.reduce() и т.п.

Из этих выводов:
/* 2 1
1 2
1 2
1 1
*/
хочу уточнить, 'a' и 'b' это 'следующий' и 'предыдущий' элементы масива?

Да, два сравниваемых элемента массива. Но как я и показал в последних примерах, это не всегда именно рядом стоящие. То есть начинается всегда с 1 и 2 элементов, но если в процессе есть такие, которые равны друг другу, то могут потом сравниваться и не соседние элементы. Но в общих чертах так.

Добавить комментарий