5 способов сортировки массивов объектов по свойствам в JavaScript
Для кого эта статья:
- Разработчики, желающие улучшить свои навыки в JavaScript
- Студенты и ученики, изучающие веб-разработку
Профессионалы, занимающиеся оптимизацией кода и производительностью веб-приложений
Работа с массивами объектов в JavaScript — это как готовка по сложному рецепту: при правильных ингредиентах и технике результат получается идеальным. Сортировка таких массивов — один из самых востребованных навыков, от которого зависит читаемость кода и производительность приложения. Массив из хаотично расположенных объектов превращается в упорядоченную структуру, с которой удобно работать. Давайте разберем пять эффективных способов сортировки массивов объектов по свойствам, которые сделают ваш код не только функциональным, но и элегантным. 💪
Изучаете JavaScript и хотите освоить продвинутые техники работы с массивами и объектами? В курсе Обучение веб-разработке от Skypro этому уделяется особое внимание. Вы не просто научитесь сортировать массивы объектов, но и поймете, как оптимизировать код для различных сценариев. Вместо поверхностного изучения теории вы получите практические навыки, которые сразу примените в реальных проектах. Ваше портфолио пополнится работами, демонстрирующими мастерство в структурировании данных.
Сортировка массива объектов по свойству: основные принципы
Массивы объектов — фундаментальная структура данных в JavaScript, с которой регулярно сталкиваются разработчики. Корректная сортировка таких массивов критически важна для множества задач: от отображения отфильтрованного списка товаров до визуализации упорядоченных аналитических данных.
Ключ к эффективной сортировке — понимание встроенного метода sort(). Этот метод изменяет исходный массив, располагая его элементы в определённом порядке. Важнейший аспект работы с sort() — функция сравнения, которая определяет логику упорядочивания. По умолчанию sort() преобразует элементы в строки и сортирует их лексикографически, что не всегда даёт желаемый результат.
Рассмотрим базовый массив объектов, который будет использоваться в наших примерах:
const users = [
{ id: 3, name: "Алексей", age: 28 },
{ id: 1, name: "Мария", age: 34 },
{ id: 5, name: "Иван", age: 22 },
{ id: 2, name: "Ольга", age: 31 },
{ id: 4, name: "Дмитрий", age: 25 }
];
Для сортировки массива объектов необходимо следовать пяти ключевым принципам:
- Принцип сравнения: создайте функцию, возвращающую отрицательное число, ноль или положительное число в зависимости от результата сравнения двух элементов.
- Иммутабельность: если требуется сохранить исходный массив, используйте
slice()для создания копии перед сортировкой. - Многоуровневая сортировка: учитывайте возможность сортировки по нескольким свойствам с установленными приоритетами.
- Обработка специальных значений: предусматривайте корректную обработку
null,undefinedи других особых значений. - Локализация: при необходимости используйте
localeCompare()для корректной сортировки строк с учётом национальных особенностей.
Базовый синтаксис сортировки массива объектов выглядит так:
array.sort((a, b) => {
// Логика сравнения объектов по нужному свойству
if (a.property < b.property) return -1;
if (a.property > b.property) return 1;
return 0;
});
Этот шаблон модифицируется в зависимости от типа сортируемых данных и требуемого порядка сортировки. Давайте рассмотрим каждый из методов более подробно. 🧩

Метод sort() для сортировки объектов по числовому свойству
Антон Корнилов, Lead Frontend Developer Однажды наша команда столкнулась с проблемой при работе над панелью администратора для интернет-магазина. Клиенты жаловались, что список товаров отображается в произвольном порядке, что затрудняло поиск конкретных позиций. Изначально мы использовали стандартный метод sort() без функции сравнения:
JSСкопировать кодproducts.sort();Это приводило к хаотичной сортировке. Мы не понимали, почему данные отображаются неправильно, пока не осознали, что sort() по умолчанию преобразует элементы в строки. Для числовых значений это давало абсурдные результаты: 10 оказывалось перед 2, потому что "10" лексикографически предшествует "2".
Решение оказалось простым — мы внедрили числовую функцию сравнения:
JSСкопировать кодproducts.sort((a, b) => a.price – b.price);Эта небольшая модификация полностью трансформировала пользовательский опыт. Клиенты перестали жаловаться, а администраторы магазина смогли эффективнее управлять товарным ассортиментом.
Сортировка по числовым свойствам — одна из наиболее распространённых задач при работе с массивами объектов. JavaScript предлагает элегантные решения для подобных сценариев. 📊
Самый распространённый метод сортировки объектов по числовому свойству использует вычитание:
// Сортировка по возрастанию возраста
const sortedByAge = users.sort((a, b) => a.age – b.age);
console.log(sortedByAge);
// Результат: отсортированный массив с Иваном (22) первым и Марией (34) последней
Для сортировки по убыванию достаточно поменять порядок операндов:
// Сортировка по убыванию возраста
const sortedByAgeDesc = users.sort((a, b) => b.age – a.age);
Это лаконичное и эффективное решение работает из-за механики sort():
- Если результат вычитания отрицательный,
aразмещается передb - Если результат положительный,
bразмещается передa - Если результат равен нулю, относительный порядок элементов сохраняется
При сортировке по числовым свойствам важно учитывать потенциальные проблемы:
| Проблема | Решение | Пример кода |
|---|---|---|
| Отсутствующие значения | Проверка на null/undefined | (a, b) => (a.age || 0) – (b.age || 0) |
| NaN значения | Конвертация в число с проверкой | (a, b) => Number(a.age || 0) – Number(b.age || 0) |
| Переполнение при больших числах | Использование Math.sign() | (a, b) => Math.sign(a.age – b.age) |
| Производительность при часто меняющихся данных | Кэширование отсортированных результатов | const cached = memoize(array => array.sort(...)) |
Для сортировки вложенных числовых свойств используется обращение через цепочку свойств:
const companies = [
{ name: "Tech Corp", metrics: { revenue: 5000000, employees: 120 } },
{ name: "Startup Inc", metrics: { revenue: 1200000, employees: 15 } },
{ name: "Enterprise Ltd", metrics: { revenue: 8500000, employees: 350 } }
];
// Сортировка по количеству сотрудников
companies.sort((a, b) => a.metrics.employees – b.metrics.employees);
Для более сложной логики сортировки можно использовать тернарные операторы или полные условные выражения:
// Сортировка с приоритетом: сначала по возрасту, затем по id
users.sort((a, b) => {
if (a.age === b.age) {
return a.id – b.id;
}
return a.age – b.age;
});
Используя эти техники, вы сможете эффективно сортировать объекты по любым числовым свойствам, от простых до самых сложных случаев. 🔢
Сортировка объектов по строковым свойствам в JavaScript
Сортировка объектов по строковым свойствам требует иного подхода, чем сортировка по числовым значениям. Текстовые данные имеют свои особенности, включая регистр символов, диакритические знаки и разные кодировки. 📝
Базовый способ сортировки по строковым свойствам использует операторы сравнения:
// Сортировка по имени (алфавитный порядок)
const sortedByName = users.sort((a, b) => {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
});
console.log(sortedByName);
// Результат: массив, отсортированный по алфавиту (Алексей, Дмитрий, Иван, ...)
Однако для корректной сортировки строк с учётом локализации рекомендуется использовать метод localeCompare():
// Сортировка по имени с учётом локализации
const sortedByNameLocale = users.sort((a, b) => a.name.localeCompare(b.name, 'ru'));
Метод localeCompare() особенно полезен при работе с многоязычными данными, так как учитывает правила сортировки для конкретного языка. Например, в русском языке "Ё" сортируется иначе, чем в простом посимвольном сравнении.
Преимущества использования localeCompare():
- Корректная обработка национальных алфавитов
- Правильная сортировка диакритических знаков
- Настраиваемая чувствительность к регистру
- Возможность указания дополнительных параметров сортировки
Пример расширенной настройки localeCompare():
users.sort((a, b) => a.name.localeCompare(b.name, 'ru', {
sensitivity: 'base', // игнорировать регистр и акценты
ignorePunctuation: true, // игнорировать знаки пунктуации
numeric: true // правильно сортировать числа в строках
}));
Для сортировки без учёта регистра без использования localeCompare() можно применить преобразование к нижнему регистру:
// Сортировка по имени без учёта регистра
users.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1);
Для сложных случаев, когда необходимо сортировать объекты по нескольким строковым свойствам, используется каскадное сравнение:
const employees = [
{ firstName: "Иван", lastName: "Петров", department: "IT" },
{ firstName: "Анна", lastName: "Сидорова", department: "HR" },
{ firstName: "Иван", lastName: "Иванов", department: "IT" }
];
// Сортировка сначала по отделу, затем по фамилии, затем по имени
employees.sort((a, b) => {
const deptCompare = a.department.localeCompare(b.department, 'ru');
if (deptCompare !== 0) return deptCompare;
const lastNameCompare = a.lastName.localeCompare(b.lastName, 'ru');
if (lastNameCompare !== 0) return lastNameCompare;
return a.firstName.localeCompare(b.firstName, 'ru');
});
| Метод сортировки строк | Преимущества | Недостатки | Рекомендуемые случаи использования |
|---|---|---|---|
| Операторы сравнения (< >) | Простота, высокая производительность | Не учитывает локализацию, проблемы с Unicode | Простые ASCII строки, высоконагруженные приложения |
| localeCompare() базовый | Поддержка локализации | Ниже производительность, чем у операторов | Многоязычные приложения |
| localeCompare() с параметрами | Максимальная гибкость настроек | Самая низкая производительность | Критичные к точности сортировки задачи |
| toLowerCase() + операторы | Нечувствительность к регистру, хорошая производительность | Не решает проблемы с диакритическими знаками | Поиск и фильтрация без учёта регистра |
При работе со строками, содержащими цифры, стоит обратить внимание на "естественную" сортировку, которая обрабатывает числовые части строк как числа:
const files = [
{ name: "file1.txt" },
{ name: "file10.txt" },
{ name: "file2.txt" }
];
// Естественная сортировка (file1.txt, file2.txt, file10.txt)
files.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true }));
Владея этими техниками, вы сможете эффективно сортировать массивы объектов по строковым свойствам с учётом всех нюансов текстовых данных. 🔡
Сравнение эффективности различных методов сортировки
Выбор метода сортировки массивов объектов может существенно влиять на производительность вашего приложения, особенно при работе с большими наборами данных. Давайте сравним эффективность различных подходов, чтобы вы могли выбрать оптимальный метод для своих задач. ⚡
Сначала рассмотрим временную сложность доступных методов сортировки:
| Метод сортировки | Временная сложность | Сохранение исходного массива | Использование памяти |
|---|---|---|---|
| array.sort() | O(n log n) в среднем | Нет (изменяет исходный массив) | O(log n) |
| [...array].sort() | O(n log n) + O(n) для копирования | Да | O(n) |
| array.slice().sort() | O(n log n) + O(n) для копирования | Да | O(n) |
| lodash sortBy | O(n log n) | Да | O(n) |
| Ручная реализация quicksort | O(n log n) в среднем, O(n²) в худшем | Зависит от реализации | O(log n) |
Результаты тестирования производительности на массиве из 10,000 объектов показывают значительные различия:
// Тестирование на большом массиве
const largeArray = Array.from({ length: 10000 }, (_, i) => ({
id: Math.floor(Math.random() * 10000),
name: `Item ${i}`,
value: Math.random() * 1000
}));
// Замер времени для разных методов
console.time('Native sort');
largeArray.sort((a, b) => a.value – b.value);
console.timeEnd('Native sort');
// Native sort: ~15ms
console.time('Spread + sort');
[...largeArray].sort((a, b) => a.value – b.value);
console.timeEnd('Spread + sort');
// Spread + sort: ~40ms
Для числовых сортировок наиболее эффективным является простой метод вычитания, но для строковых сравнений ситуация сложнее:
// Сравнение методов строковой сортировки
console.time('Direct comparison');
largeArray.sort((a, b) => a.name > b.name ? 1 : -1);
console.timeEnd('Direct comparison');
// Direct comparison: ~18ms
console.time('localeCompare basic');
largeArray.sort((a, b) => a.name.localeCompare(b.name));
console.timeEnd('localeCompare basic');
// localeCompare basic: ~80ms
console.time('localeCompare with options');
largeArray.sort((a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }));
console.timeEnd('localeCompare with options');
// localeCompare with options: ~95ms
Михаил Сергеев, JavaScript Performance Engineer В проекте по созданию интерактивной таблицы с тысячами строк данных мы столкнулись с серьезными проблемами производительности. Пользователи жаловались на зависания интерфейса при сортировке данных по различным столбцам.
Изначально мы использовали стандартный подход с localeCompare для строковых полей:
JSСкопировать кодdata.sort((a, b) => a.customerName.localeCompare(b.customerName, 'ru', { sensitivity: 'base' }));Профилирование показало, что именно этот код вызывал задержки до 500 мс при каждой сортировке. Мы оптимизировали решение, создав кэширующую функцию сортировки:
JSСкопировать кодconst cachedSort = memoize((array, property, direction) => { const multiplier = direction === 'asc' ? 1 : -1; return [...array].sort((a, b) => { // Для строк используем простое сравнение в большинстве случаев return multiplier * (a[property] > b[property] ? 1 : -1); }); });Это решение снизило время сортировки до 50 мс — улучшение в 10 раз! Для случаев, где требовалась корректная локализация, мы применяли localeCompare только по явному требованию пользователя через настройки.
Главный вывод: не используйте сложные методы сортировки без необходимости. Часто простое лексикографическое сравнение достаточно эффективно для большинства задач.
Практические рекомендации по оптимизации сортировки:
- Используйте мемоизацию для кэширования результатов сортировки, если исходные данные меняются нечасто.
- Сортируйте только видимые данные при работе с пагинацией, а не весь массив.
- Избегайте повторной сортировки при каждом рендеринге в React или других фреймворках.
- Предварительно вычисляйте ключи сортировки для сложных функций сравнения.
- Используйте Web Workers для сортировки больших массивов в фоновом потоке.
Пример оптимизации через предварительное вычисление ключей:
// Неоптимальный вариант для больших массивов
array.sort((a, b) => complexFunction(a) – complexFunction(b));
// Оптимизированный вариант
const arrayWithKeys = array.map(item => ({
original: item,
sortKey: complexFunction(item)
}));
arrayWithKeys.sort((a, b) => a.sortKey – b.sortKey);
const result = arrayWithKeys.map(item => item.original);
Выбирая метод сортировки, всегда учитывайте конкретный сценарий использования, объем данных и требования к локализации, чтобы найти оптимальный баланс между корректностью и производительностью. 🚀
Продвинутые техники сортировки массивов объектов
Для решения сложных задач сортировки массивов объектов в JavaScript требуются продвинутые техники, которые выходят за рамки базового применения метода sort(). Рассмотрим пять мощных подходов, которые позволят вам создавать гибкие и масштабируемые решения. 🔧
1. Динамическая сортировка по произвольному свойству
Этот паттерн позволяет создавать универсальные функции сортировки, которые могут работать с любым свойством объекта:
// Функция динамической сортировки
function dynamicSort(property, order = 'asc') {
const sortOrder = order === 'asc' ? 1 : -1;
return function(a, b) {
const aValue = a[property];
const bValue = b[property];
// Обработка null и undefined
if (aValue == null) return sortOrder;
if (bValue == null) return -sortOrder;
// Выбор метода сравнения в зависимости от типа
if (typeof aValue === 'string' && typeof bValue === 'string') {
return sortOrder * aValue.localeCompare(bValue);
}
return sortOrder * (aValue < bValue ? -1 : (aValue > bValue ? 1 : 0));
};
}
// Использование
users.sort(dynamicSort('age', 'desc')); // Сортировка по возрасту по убыванию
users.sort(dynamicSort('name')); // Сортировка по имени по возрастанию
2. Сортировка по вложенным свойствам с использованием путей к данным
Для работы со сложными объектами, содержащими вложенные структуры, полезна функция сортировки, которая принимает путь к свойству в нотации через точку:
function sortByPath(path, order = 'asc') {
const sortOrder = order === 'asc' ? 1 : -1;
return function(a, b) {
// Разбиваем путь на сегменты
const segments = path.split('.');
// Получаем значения по указанному пути
let aValue = a;
let bValue = b;
for (const segment of segments) {
aValue = aValue?.[segment];
bValue = bValue?.[segment];
// Если путь прервался, прекращаем обход
if (aValue === undefined) break;
if (bValue === undefined) break;
}
// Далее логика сравнения как в dynamicSort
if (aValue == null) return sortOrder;
if (bValue == null) return -sortOrder;
if (typeof aValue === 'string' && typeof bValue === 'string') {
return sortOrder * aValue.localeCompare(bValue);
}
return sortOrder * (aValue < bValue ? -1 : (aValue > bValue ? 1 : 0));
};
}
// Пример использования
const complexUsers = [
{ id: 1, profile: { personal: { firstName: 'Иван', lastName: 'Петров' } } },
{ id: 2, profile: { personal: { firstName: 'Мария', lastName: 'Иванова' } } }
];
complexUsers.sort(sortByPath('profile.personal.lastName'));
3. Мультисортировка с конфигурируемым приоритетом полей
Этот метод позволяет сортировать по нескольким полям с указанием порядка сортировки для каждого:
function multiSort(sortCriteria) {
return (a, b) => {
// Проходим по всем критериям сортировки
for (const { key, order } of sortCriteria) {
const direction = order === 'asc' ? 1 : -1;
const aValue = typeof key === 'function' ? key(a) : a[key];
const bValue = typeof key === 'function' ? key(b) : b[key];
// Пропускаем равные значения
if (aValue === bValue) continue;
// Сравниваем по текущему критерию
if (typeof aValue === 'string') {
return direction * aValue.localeCompare(bValue);
}
return direction * (aValue < bValue ? -1 : 1);
}
return 0; // Все значения равны
};
}
// Пример использования
users.sort(multiSort([
{ key: 'age', order: 'asc' },
{ key: 'name', order: 'desc' }
]));
// Можно использовать функции для извлечения значений
users.sort(multiSort([
{ key: user => user.profile?.rank || 0, order: 'desc' },
{ key: 'name', order: 'asc' }
]));
4. Стабильная сортировка с сохранением исходного порядка при равенстве
Для создания стабильной сортировки, которая сохраняет исходный порядок элементов при равенстве сравниваемых свойств:
function stableSort(array, compareFn) {
// Добавляем индексы к элементам
const indexed = array.map((item, index) => ({ item, index }));
// Сортируем с учетом исходных индексов
indexed.sort((a, b) => {
const compareResult = compareFn(a.item, b.item);
// При равенстве основного критерия используем исходный порядок
return compareResult !== 0 ? compareResult : a.index – b.index;
});
// Возвращаем исходные элементы в новом порядке
return indexed.map(({ item }) => item);
}
// Пример использования
const stableSorted = stableSort(users, (a, b) => a.age – b.age);
5. Сортировка с учетом весов и комбинированных критериев
Этот подход позволяет сортировать элементы по комбинированному критерию с весами:
function weightedSort(criteriaWithWeights) {
return (a, b) => {
let totalDifference = 0;
for (const { key, weight, normalize } of criteriaWithWeights) {
const aValue = typeof key === 'function' ? key(a) : a[key];
const bValue = typeof key === 'function' ? key(b) : b[key];
// Опциональная нормализация значений
const normalizedA = normalize ? normalize(aValue) : aValue;
const normalizedB = normalize ? normalize(bValue) : bValue;
// Вычисляем взвешенную разницу
const difference = typeof normalizedA === 'string'
? normalizedA.localeCompare(normalizedB)
: normalizedA – normalizedB;
totalDifference += difference * weight;
}
return totalDifference;
};
}
// Пример использования
const sortedByMultipleCriteria = users.sort(weightedSort([
{ key: 'age', weight: 0.7, normalize: age => age / 100 },
{ key: user => user.orders?.length || 0, weight: 0.3 }
]));
Применяя эти продвинутые техники, вы сможете создавать гибкие и мощные системы сортировки для любых сценариев использования. Главное преимущество этих подходов — возможность абстрагировать логику сортировки от конкретных данных, что делает код более универсальным и поддерживаемым. 🧠
Эти методы особенно полезны в сложных приложениях с большими объемами данных, таких как панели управления, аналитические инструменты и интерактивные таблицы с возможностью пользовательской настройки сортировки. 🚀
Владение различными методами сортировки массивов объектов в JavaScript — неотъемлемый навык современного разработчика. От базового использования метода sort() до сложных динамических техник — каждый подход имеет свои преимущества и области применения. Выбирая оптимальный метод, основывайтесь на характере данных, требованиях к локализации и производительности. Помните, что самый элегантный код не всегда самый эффективный. Тестируйте различные подходы на реальных данных и не бойтесь комбинировать методы для достижения идеального баланса между читаемостью и производительностью.