Если вам нужно рассортировать массив объектов в определенном порядке, возможно, вы почувствуете искушение применить библиотеку 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.js, Lodash и Sugar). Нет ничего сложного самостоятельно применить такой функционал.
Статью перевел timurziev. Оригинал на Www.sitepoint.com доступен по ссылке.