11.03.2017 5244 1

Сортировка массива объектов в 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 доступен по ссылке.