Сортировка массива объектов в JavaScript

Сортировка массива объектов в JavaScript
Сортировка массива объектов в JavaScript

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

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

По умолчанию, функция Array.sort в JavaScript конвертирует каждый элемент в массиве в строку, а затем сравнивает их в порядке Unicode code point.

const foo = [9, 2, 3, 'random', 'panda'];
foo.sort(); // returns [ 2, 3, 9, 'panda', 'random' ]

const bar = [4, 19, 30, function(){}, {key: 'value'}];
bar.sort(); // returns [ 19, 30, 4, { key: 'value' }, [Function] ]

Немного странно, почему 30 идет перед 4… не логично, да? На самом деле все логично. Это происходит по причине того, что каждый элемент в массиве сначало конвертируется в строку, и «30» находится перед «4» в порядке Unicode.

Заметьте, что в отличии от многих других функций в JavaScript, функция Array.sort изменяет или мутирует сортируемый массив.

const baz = ['hello world', 31, 5, 9, 12];
baz.sort(); // массив baz модифицирован
console.log(baz); // показывает [12, 31, 5, 9, "hello world"]

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

const baz = ['hello world', 31, 5, 9, 12];
const newBaz = baz.slice().sort(); // создаем и сортируем новый const baz
console.log(baz); // "hello world", 31, 5, 9, 12]
console.log(newBaz); // [12, 31, 5, 9, "hello world"]

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

Используем функцию сравнения для сортировки

Допустим a и b — два элемента сравниваемых в функции. Если возвращаемое значение будет:

1 меньше 0 — a идет перед b

2 больше 0 — b идет перед a

3 равно 0 — a и b не изменяются

Рассмотрим простой пример:

const arr = [1,2,30,4];

function compare(a, b){
  let comparison = 0;

  if (a > b) {
    comparison = 1;
  } else if (b > a) {
    comparison = -1;
  }

  return comparison;
}

arr.sort(compare);
// => 1, 2, 4, 30

Код можно рефакторить для получения возвращаемого значения, извлекая a из b

function compare(a, b){
  return a - b;
}

Теперь можно создать стрелочную функцию:

arr.sort((a, b) => a - b));

Сортировка массива объектов в JavaScript

А теперь перейдем к сортировке массива объектов. Возьмем следующий пример:

const bands = [ 
  { genre: 'Rap', band: 'Migos', albums: 2},
  { genre: 'Pop', band: 'Coldplay', albums: 4},
  { genre: 'Rock', band: 'Breaking Benjamins', albums: 1}
];

Мы используем функцию сравнения для сортировки массива согласно жанру:

function compare(a, b) {
  // Используем toUpperCase() для преобразования регистра
  const genreA = a.genre.toUpperCase();
  const genreB = b.genre.toUpperCase();

  let comparison = 0;
  if (genreA > genreB) {
    comparison = 1;
  } else if (genreA < genreB) {
    comparison = -1;
  }
  return comparison;
}

bands.sort(compare);

/* returns [ 
{ genre: 'Pop', band: 'Coldplay', albums: 4 }, 
{ genre: 'Rap', band: 'Migos', albums: 2 }, 
{ genre: 'Rock', band: 'Breaking Benjamins', albums: 1 } 
] */

Чтобы поменять порядок, мы можем инвертировать возвращаемое значение:

function compare(a, b) {
  ...

  //инвертируем возвращаемое значение умножая на -1
  return comparison * -1; 
}

Создаем динамическую функцию сортировки

Закончим на более динамичном примере. Создадим функцию, с помощью которой можно сортировать массив объектов, чьи значения не являются строками или числами. Функция имеет два параметра — ключ, которым мы сортируем и порядок получаемого результата (ascending или descending).

const bands = [ 
  { genre: 'Rap', band: 'Migos', albums: 2},
  { genre: 'Pop', band: 'Coldplay', albums: 4, awards: 13},
  { genre: 'Rock', band: 'Breaking Benjamins', albums: 1}
];

// функция динамической сортировки
function compareValues(key, order='asc') {
  return function(a, b) {
    if(!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) {
      // свойства нет ни в одном из объектов
        return 0; 
    }

    const varA = (typeof a[key] === 'string') ? 
      a[key].toUpperCase() : a[key];
    const varB = (typeof b[key] === 'string') ? 
      b[key].toUpperCase() : b[key];

    let comparison = 0;
    if (varA > varB) {
      comparison = 1;
    } else if (varA < varB) {
      comparison = -1;
    }
    return (
      (order == 'desc') ? (comparison * -1) : comparison
    );
  };
}

Вот как мы это используем:

// сортировка массива по band, по умолчанию в порядке ascending
bands.sort(compareValues('band')); 

// сортировка массива по band в порядке descending
bands.sort(compareValues('band', 'desc')); 

// сортировка массива по альбому в порядке ascending
bands.sort(compareValues('albums')); 

Выше мы используем метод hasOwnProperty чтобы проверить, установлено ли указанное свойство в каждом объекте и не было ли оно унаследовано через прототип цепи. Если он не указан в объекте, функция вернет 0, что оставляет порядок сортировки как есть (объекты остаются неизменными).

Typeof operator также используется для проверки типа даты данного значения. Это позволяет нам определить правильный способ сортировки массива. К примеру, если значение указанного свойства является строкой, метод toUpperCase используется для конвертирования всех символов в верхний регистр, поэтому происходит игнорирование регистра при сортировки.

Заключение

Вот и все. Короткое знакомство с сортировкой массива объектов. Многие библиотеки в JavaScript предлагают свои методы сортировки (Underscore.jsLodash и Sugar). Нет ничего сложного самостоятельно применить такой функционал.

Статью перевел timurziev. Оригинал на Www.sitepoint.com доступен по ссылке.

Автор

MIIIG

Занимаюсь разработкой сайтов, знаю php, js, html, css, gulp, bash, увлекаюсь разработкой на микроконтроллерах(esp, arduino), перевожу статьи, также увлечен графическим дизайном, типографией, знаю также немного арабский, люблю минимализм, простоту и material design