← Назад к вопросам

Группировка массива объектов по ключу

1.0 Junior🔥 251 комментариев
#React

Условие

Напишите функцию groupBy(arr, key), которая группирует массив объектов по значению указанного ключа.

Требования

  1. Функция принимает массив объектов и имя ключа для группировки
  2. Возвращает объект, где ключи - уникальные значения указанного поля
  3. Значения - массивы объектов с соответствующим значением ключа
  4. Временная сложность должна быть O(n)

Примеры

const people = [
  { name: "Alice", city: "Moscow" },
  { name: "Bob", city: "SPb" },
  { name: "Charlie", city: "Moscow" },
  { name: "Diana", city: "SPb" }
];

groupBy(people, "city");
// Результат:
// {
//   "Moscow": [{ name: "Alice", city: "Moscow" }, { name: "Charlie", city: "Moscow" }],
//   "SPb": [{ name: "Bob", city: "SPb" }, { name: "Diana", city: "SPb" }]
// }

Бонус

Добавьте поддержку функции вместо ключа: groupBy(arr, item => item.city).

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Решение: Группировка массива объектов по ключу

Практическое применение

Группировка данных — один из самых часто используемых операций при обработке массивов. Она применяется при фильтрации по категориям, агрегировании статистики, организации данных для отображения. Эта функция работает за один проход массива с O(n) сложностью.

Базовое решение

function groupBy(arr, key) {
  return arr.reduce((result, item) => {
    const groupKey = item[key];
    
    if (!result[groupKey]) {
      result[groupKey] = [];
    }
    
    result[groupKey].push(item);
    return result;
  }, {});
}

Версия с поддержкой функции

function groupBy(arr, keyOrFn) {
  return arr.reduce((result, item) => {
    // Определяем функцию получения ключа
    const getKey = typeof keyOrFn === 'function' ? keyOrFn : (obj) => obj[keyOrFn];
    const groupKey = getKey(item);
    
    if (!result[groupKey]) {
      result[groupKey] = [];
    }
    
    result[groupKey].push(item);
    return result;
  }, {});
}

TypeScript версия

type GroupKey = string | number | symbol;
type GroupByFn<T> = (item: T) => GroupKey;
type GroupByKey<T> = keyof T;
type GroupBySelector<T> = GroupByKey<T> | GroupByFn<T>;

function groupBy<T extends Record<string, any>>(
  arr: T[],
  selector: GroupBySelector<T>
): Record<GroupKey, T[]> {
  const getKey = typeof selector === 'function' 
    ? selector as GroupByFn<T>
    : (item: T) => item[selector as GroupByKey<T>];
  
  return arr.reduce((result, item) => {
    const groupKey = String(getKey(item));
    
    if (!result[groupKey]) {
      result[groupKey] = [];
    }
    
    result[groupKey].push(item);
    return result;
  }, {} as Record<GroupKey, T[]>);
}

Примеры использования

// Базовый пример с ключом
const people = [
  { name: "Alice", city: "Moscow" },
  { name: "Bob", city: "SPb" },
  { name: "Charlie", city: "Moscow" },
  { name: "Diana", city: "SPb" }
];

const byCity = groupBy(people, "city");
console.log(byCity);
// {
//   "Moscow": [
//     { name: "Alice", city: "Moscow" },
//     { name: "Charlie", city: "Moscow" }
//   ],
//   "SPb": [
//     { name: "Bob", city: "SPb" },
//     { name: "Diana", city: "SPb" }
//   ]
// }

// С функцией как селектором
const byAge = groupBy(people, (person) => {
  if (person.age < 20) return "young";
  if (person.age < 40) return "adult";
  return "senior";
});

// Группировка по типу
const items = [
  { id: 1, type: "fruit", name: "apple" },
  { id: 2, type: "vegetable", name: "carrot" },
  { id: 3, type: "fruit", name: "banana" }
];

const byType = groupBy(items, "type");
// {
//   "fruit": [{id: 1, ...}, {id: 3, ...}],
//   "vegetable": [{id: 2, ...}]
// }

// Сложная функция группировки
const orders = [
  { id: 1, status: "completed", amount: 100 },
  { id: 2, status: "pending", amount: 200 },
  { id: 3, status: "completed", amount: 150 }
];

const grouped = groupBy(orders, (order) => `${order.status}_${order.amount > 150 ? 'large' : 'small'}`);

Версия Map для уникальных типов

function groupBy(arr, keyOrFn) {
  const map = new Map();
  const getKey = typeof keyOrFn === 'function' ? keyOrFn : (obj) => obj[keyOrFn];
  
  arr.forEach((item) => {
    const groupKey = getKey(item);
    
    if (!map.has(groupKey)) {
      map.set(groupKey, []);
    }
    
    map.get(groupKey).push(item);
  });
  
  return Object.fromEntries(map);
}

Версия с использованием findIndex

function groupBy(arr, keyOrFn) {
  const getKey = typeof keyOrFn === 'function' ? keyOrFn : (obj) => obj[keyOrFn];
  
  return arr.reduce((result, item) => {
    const key = getKey(item);
    const group = result.find((g) => Object.keys(g)[0] === key);
    
    if (group) {
      group[key].push(item);
    } else {
      result.push({ [key]: [item] });
    }
    
    return result;
  }, []);
}

Расширенная версия с опциями

function groupBy(arr, selector, options = {}) {
  const {
    mapValue = (items) => items,
    keySeparator = "_",
    multiKey = false
  } = options;
  
  const getKey = typeof selector === 'function' ? selector : (obj) => obj[selector];
  const result = {};
  
  arr.forEach((item) => {
    const key = String(getKey(item));
    
    if (!result[key]) {
      result[key] = [];
    }
    
    result[key].push(item);
  });
  
  // Применяем mapValue к каждой группе
  Object.keys(result).forEach((key) => {
    result[key] = mapValue(result[key]);
  });
  
  return result;
}

// Использование с опциями
const grouped = groupBy(people, "city", {
  mapValue: (items) => items.map((item) => item.name)
});
// { "Moscow": ["Alice", "Charlie"], "SPb": ["Bob", "Diana"] }

Пошаговый разбор примера

const arr = [{name: "A", city: "Moscow"}, {name: "B", city: "SPb"}, {name: "C", city: "Moscow"}];

// Итерация 1: item = {name: "A", city: "Moscow"}, groupKey = "Moscow"
// result = {"Moscow": [{name: "A", city: "Moscow"}]}

// Итерация 2: item = {name: "B", city: "SPb"}, groupKey = "SPb"
// result = {"Moscow": [...], "SPb": [{name: "B", city: "SPb"}]}

// Итерация 3: item = {name: "C", city: "Moscow"}, groupKey = "Moscow"
// result = {"Moscow": [{name: "A", ...}, {name: "C", ...}], "SPb": [...]}

Сравнение подходов

ПодходПлюсыМинусы
Object (string ключи)Просто, быстроТолько строковые ключи
MapЛюбые ключи, сохраняет порядокСложнее преобразовать
Reduce с объектомСтандартный patternНужна проверка существования
Reduce с findIndexСохраняет порядок вставкиМедленнее O(n²)

Реальные применения

  • Фильтрация по категориям: группировка товаров по типу
  • Отчёты: группировка данных по месяцам, регионам
  • UI компоненты: группировка опций в select
  • Аналитика: агрегирование событий по типу

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

  • reduce(): однопроходный алгоритм O(n)
  • Функция или ключ: гибкость в выборе критерия группировки
  • Проверка существования: инициализируем массив если ключа нет
  • String() преобразование: для сложных типов ключей
  • Расширяемость: легко добавить mapValue для трансформации групп
Группировка массива объектов по ключу | PrepBro