Как написать компаратор для сортировки?

158
30 апреля 2019, 05:40

Как отсортировать числовой массив?
Как отсортировать массив объектов по нескольким полям?

Answer 1
Стандартная сортировка

Функция sort по умолчанию приводит элементы массива к строкам, а затем сортирует.

[1,-8,90,32,0,0,5,92,900].sort()
// [-8, 0, 0, 1, 32, 5, 90, 900, 92]

Если массив состоит из чисел, то такое поведение является нежелательным.

Использование компаратора

Функция sort может принимать аргумент - компаратор.

Компаратор - это функция, которая принимает 2 аргумента и возвращает отрицательное число, если первый меньше, положительное, если второй меньше и 0, если они равны.

Для чисел достаточно распространено использование вычитания в качестве компаратора:

[1,-8,90,32,0,0,5,92,900].sort(function(a, b) {
  return a-b;
})
// [-8, 0, 0, 1, 5, 32, 90, 92, 900]

Вообще, следует с осторожностью относиться к этой конструкции из-за возможного переполнения, но в большинстве случаев она работает.

Компаратор на основе сравнения

Можно вспомнить, что во многих языках true приводится к 1, а false - к 0.

Воспользуемся этой возможностью. Только одно из условий a<b и b<a истинно, поэтому разность (b<a) - (a<b) замечательно подходит на роль компаратора:

[1,-8,90,32,0,0,5,92,900].sort(function(a, b) {
  return (b<a) - (a<b);
})
// [-8, 0, 0, 1, 5, 32, 90, 92, 900]

Какие тут плюсы? Помимо невозможности переполнения, такое сравнение можно использовать не только с числами. Например, строки вычитать мы не можем, а сравнивать можем.

Для сортировки по убыванию надо просто поменять сравнения местами:

[1,-8,90,32,0,0,5,92,900].sort(function(a, b) {
  return (a<b) - (b<a);
})
// [900, 92, 90, 32, 5, 1, 0, 0, -8]
Сортировка по нескольким полям

При сравнении по нескольким полям требуется, чтобы следующее поле сравнивалось только когда предыдущие равны. Это значит, что часть компаратора, выполняющая сравнение тех полей при вычислении даёт 0.

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

Отсортируем массив по имени и id:

[{name:"John", id:7}, {name:"John",id:4}, {name:"Adam",id:3}, {name:"Adam",id:30}, {name:"Rose",id:1}].sort(function(a, b) {
  return (b.name<a.name) - (a.name<b.name) || (b.id<a.id) - (a.id<b.id);
})
// [{"name":"Adam","id":3},{"name":"Adam","id":30},{"name":"John","id":4},{"name":"John","id":7},{"name":"Rose","id":1}]
READ ALSO
Как получить данные в php из $.post

Как получить данные в php из $.post

Как получить значения name, phone, email в feedbackphp?

267
Не записывается объект в массив в Vue.js

Не записывается объект в массив в Vue.js

Подскажите, пожалуйста, почему в переменную thischaracter2[i] записывается только один объект?

146
Как сделать подгрузку данных с помощью Intersection Observer?

Как сделать подгрузку данных с помощью Intersection Observer?

Всем приветПрочитал про такое api, как Intersection Observer, но использовать это, почему-то не состояние

153
Система счисления?

Система счисления?

функция parseInt превращает из 16 системы счисления в 10 систему счисления, а можно наоборот ? Или нужно использовать toString ?

121