[undefined, null, NaN].sort (); или как не сортировать массивы

  • 11 сентября, 13:40
  • 5585
  • 0

Как не сортировать массивы в JavaScript

Сортировка массивов - это одна из тех вещей, о которой вы не слишком долго думаете, пока она не начинает проблемно работать. Недавно я работал с массивом элементов в JavaScript, которые не сортировались должным образом и полностью портили интерфейс. 

[undefined, null, NaN] .sort (); или как не сортировать массивы

Мне потребовалось слишком много времени, чтобы понять, что пошло не так, поэтому я хотел поделиться тем, что случилось и почему это было так странно.

Базовая сортировка

JavaScript имеет sort метод, доступный на объектах Array, и его запуск, вероятно, сделает то, что вы ожидаете. Например:

const stringArray = ['cat', 'dog', 'ant', 'butterfly'];
stringArray.sort();
// => [ 'ant', 'butterfly', 'cat', 'dog' ]

Это даже очень хорошо, если вы сортируете массивы, в которых могут быть undefined, MDN говорит, что «все неопределенные элементы отсортированы до конца массива».

const stringArrayWithUndefined = [
  'cat',
  undefined,
  'dog',
  undefined,
  'ant',
  'butterfly',
  'zebra'
];
stringArrayWithUndefined.sort();
// => [ 'ant', 'butterfly', 'cat', 'dog', 'zebra', undefined, undefined ]

Ошибки

Первая проблема, с которой вы можете столкнуться, - это обнаружение массива, содержащего null,

const stringArrayWithUndefinedAndNull = [
  'cat',
  undefined,
  'dog',
  undefined,
  'ant',
  null,
  'butterfly',
  'zebra'
];
stringArrayWithUndefinedAndNull.sort();
// => [ 'ant', 'butterfly', 'cat', 'dog', null, 'zebra', undefined, undefined ]

Сортировка приведет null в строку «ноль», которая появится где-то в середине алфавита.

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

const numberArray = [5, 3, 7, 1];
numberArray.sort();
// => [ 1, 3, 5, 7 ]


const biggerNumberArray = [5, 3, 10, 7, 1];
biggerNumberArray.sort();
// => [ 1, 10, 3, 5, 7 ]

В приведенном выше примере 10 сортируется до 3, потому что «10» преобразуется только в 1 символ и 0 просто не воспринимается. Даже если вы напишете 222, то воспримется только первое 2.

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

Для сортировки чисел в порядке возрастания функция сравнения относительно проста:

const compareNumbers = (a, b) => a - b;

Вычитание первого элемента из второго удовлетворяет требования выше. Используя эту функцию сравнения с нашими biggerNumberArray мы получим правильную сортировку:

biggerNumberArray.sort(compareNumbers);
// => [ 1, 3, 5, 7, 10 ]

Это все еще работает, если у вас есть undefined элементы, поскольку они игнорируются и сортируются до конца.

const numberArrayWithUndefined = [5, undefined, 3, 10, 7, 1];
numberArrayWithUndefined.sort(compareNumbers);
// => [ 1, 3, 5, 7, 10, undefined ]

null вызывает проблемы снова, хотя.

const numberArrayWithUndefinedAndNull = [5, undefined, 3, null, 10, 7, 1];
numberArrayWithUndefinedAndNull.sort(compareNumbers);
// => [ null, 1, 3, 5, 7, 10, undefined ]

Это происходит потому, что принуждение null на номер возвращается 0.

Number(null);
// => 0

Непоследовательные ошибки

Как мы уже видели, если массив содержит undefined это игнорируется и просто сортируется вконец. Тем не менее, если вы сортируете объекты, где ключи могут быть undefined эта автоматическая сортировка не происходит, и результаты становятся противоречивыми.

Например, если у вас есть массив объектов, некоторые из которых имеют значения, а некоторые нет, попытка сортировки по этому значению не даст желаемого результата.

const objectArray = [
  { value: 1 },
  { value: 10 },
  {},
  { value: 5 },
  { value: 7 },
  { value: 3 }
];
const compareObjects = (a, b) => a.value - b.value;
objectArray.sort(compareObjects);
// => [ { value: 1 },
//      { value: 10 },
//      {},
//      { value: 3 },
//      { value: 5 },
//      { value: 7 } ]

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

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

objectArray
  .filter(obj => typeof obj.value !== 'undefined')
  .sort(compareObjects);
// => [ { value: 1 },
//      { value: 3 },
//      { value: 5 },
//      { value: 7 },
//      { value: 10 } ]

Остерегайтесь сортировки

Результатом всего этого является то, что функция сортировки не так проста, как может показаться. Строки работают, числа нуждаются в некотором вводе, и хотя undefined обрабатывается как примитив, вы должны следить за null или undefined значения объекта.


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

IT Новости

Смотреть все