Сортировка и фильтрация массива

Паттерны фильтрации, поиска и сортировки массива объектов — готовые сниппеты для таблиц, каталогов и списков.

Задача

Нужно отображать список товаров/пользователей с возможностью фильтрации по полю, поиска по тексту и сортировки по колонке — без сторонних библиотек.

Решение

const products = [
  { id: 1, name: 'Ноутбук',  category: 'electronics', price: 80000, rating: 4.5 },
  { id: 2, name: 'Футболка', category: 'clothing',     price: 1500,  rating: 4.1 },
  { id: 3, name: 'Смартфон', category: 'electronics', price: 60000, rating: 4.8 },
  { id: 4, name: 'Джинсы',   category: 'clothing',     price: 3500,  rating: 3.9 },
  { id: 5, name: 'Планшет',  category: 'electronics', price: 40000, rating: 4.3 },
];

// === ФИЛЬТРАЦИЯ ===

// По категории
const electronics = products.filter((p) => p.category === 'electronics');

// По диапазону цены
const affordable = products.filter((p) => p.price >= 1000 && p.price <= 50000);

// Текстовый поиск по названию
function search(items, query) {
  const q = query.toLowerCase().trim();
  return q ? items.filter((p) => p.name.toLowerCase().includes(q)) : items;
}

// Несколько фильтров одновременно
function applyFilters(items, { category, minPrice, maxPrice, query }) {
  return items
    .filter((p) => !category || p.category === category)
    .filter((p) => !minPrice || p.price >= minPrice)
    .filter((p) => !maxPrice || p.price <= maxPrice)
    .filter((p) => !query || p.name.toLowerCase().includes(query.toLowerCase()));
}

const filtered = applyFilters(products, {
  category: 'electronics',
  maxPrice: 70000,
  query: '',
});

// === СОРТИРОВКА ===

// По цене (по возрастанию)
const byPriceAsc  = [...products].sort((a, b) => a.price - b.price);
const byPriceDesc = [...products].sort((a, b) => b.price - a.price);

// По строке (локализованная)
const byName = [...products].sort((a, b) => a.name.localeCompare(b.name, 'ru'));

// Универсальная сортировка по ключу
function sortBy(items, key, direction = 'asc') {
  return [...items].sort((a, b) => {
    const va = a[key];
    const vb = b[key];

    if (typeof va === 'string') {
      const cmp = va.localeCompare(vb, 'ru');
      return direction === 'asc' ? cmp : -cmp;
    }

    return direction === 'asc' ? va - vb : vb - va;
  });
}

const sorted = sortBy(products, 'rating', 'desc');

Полный пайплайн фильтр + сортировка:

function processItems(items, filters, sortKey, sortDir) {
  const filtered = applyFilters(items, filters);
  return sortBy(filtered, sortKey, sortDir);
}

Ключевые моменты

  • [...products].sort() — всегда копируй массив (spread или slice); нативный sort мутирует оригинал.
  • localeCompare('ru') — правильная сортировка кириллицы; без него буква «Я» окажется перед «А».
  • Цепочка .filter.filter — читаема и легко расширяема; производительность приемлема до ~10k элементов.
  • Для больших массивов (100k+) рассмотри Web Worker или серверную сортировку.

Варианты

  • Array.prototype.toSorted (ES2023) — возвращает новый отсортированный массив без мутации.
  • Intl.Collator — быстрее localeCompare при многократной сортировке больших массивов.

Связанные рецепты / темы