Группировка массивов в JavaScript (2024)

Перевод статьи «Array Grouping in JavaScript (2024)».

Группировка массивов — это задача, которую вы, скорее всего, реализовывали в JavaScript. Она напоминает выполнение GROUP BY в SQL. Имея набор данных, мы можем составить набор более высокого уровня, поместив похожие данные в группы и присвоив группам идентификаторы.

В этой статье я рассмотрю новые функции группировки массивов, вышедшие в 2024 году, а именно Object.groupBy и Map.groupBy.

Что касается TypeScript, поддержка этих API доступна начиная с TypeScript 5.4. Однако вам придется настроить tsconfig.json на ESNext. Когда они будут доступны в ES2024, вы сможете перейти на ES2024 или выше.

Группировка массивов до 2024 года

Группировка массивов в JavaScript — не новая концепция, мы уже реализовывали ее в различных вариантах. Например, можно применить циклы for или foreach, Array.prototype.reduce или функции groupBy в underscore.js или lodash.

Вот как мы группируем данные, используя функцию reduce(), если у нас есть список сотрудников:

interface Employee {
  name: string;
  department: string;
  age: number;
  manager?: Employee;
  joined: Date
}

const ceo = {
    name: "John Doe",
    department: "engineering",
    age: 47,
    joined: new Date("10-04-2020")
}

const cfo = {
    name: "Anna Maria",
    department: "finance",
    age: 45,
    joined: new Date("10-05-2020")
  }

 const employees: Employee[] = [
  ceo,
  {
    name: "Pop Jones Jr.",
    department: "finance",
    age: 30,
    manager: cfo,
    joined: new Date("10-04-2021")
  },
  {
    name: "Sarah Clara",
    department: "engineering",
    age: 32,
    manager: ceo,
    joined: new Date("10-05-2021")
  },
  cfo,
  {
    name: "Funmi Kola",
    department: "engineering",
    age: 20,
    manager: ceo,
    joined: new Date("10-05-2022")
  },
  {
    name: "Julius Maria",
    department: "sales",
    age: 27,
    manager: cfo,
    joined: new Date("10-05-2022")
  }
 ]

const groupByDepartment = employees.reduce<Record<string, Employee[]>>((acc, employee) => {
  const department = employee.department;
  if (acc[department] === undefined) {
    acc[department] = [];
  }

  acc[department].push(employee);
  return acc;
}, {});

console.log(groupByDepartment);

Вывод для groupByDepartment должен быть таким:

{
    "engineering": [
        {
            "name": "John Doe",
            "department": "engineering",
            "age": 47,
            "joined": "2020-10-03T22:00:00.000Z"
        },
        {
            "name": "Sarah Clara",
            "department": "engineering",
            "age": 32,
            "manager": {
                "name": "John Doe",
                "department": "engineering",
                "age": 47,
                "joined": "2020-10-03T22:00:00.000Z"
            },
            "joined": "2021-10-04T22:00:00.000Z"
        },
        {
            "name": "Funmi Kola",
            "department": "engineering",
            "age": 20,
            "manager": {
                "name": "John Doe",
                "department": "engineering",
                "age": 47,
                "joined": "2020-10-03T22:00:00.000Z"
            },
            "joined": "2022-10-04T22:00:00.000Z"
        }
    ],
    "finance": [
        {
            "name": "Pop Jones Jr.",
            "department": "finance",
            "age": 30,
            "manager": {
                "name": "Anna Maria",
                "department": "finance",
                "age": 45,
                "joined": "2020-10-04T22:00:00.000Z"
            },
            "joined": "2021-10-03T22:00:00.000Z"
        },
        {
            "name": "Anna Maria",
            "department": "finance",
            "age": 45,
            "joined": "2020-10-04T22:00:00.000Z"
        }
    ],
    "sales": [
        {
            "name": "Julius Maria",
            "department": "sales",
            "age": 27,
            "manager": {
                "name": "Anna Maria",
                "department": "finance",
                "age": 45,
                "joined": "2020-10-04T22:00:00.000Z"
            },
            "joined": "2022-10-04T22:00:00.000Z"
        }
    ]
}

Группировка с помощью Object.groupBy

Функция Object.groupBy используется для группировки элементов итерируемого объекта по значению, которое возвращается предоставленной колбэк-функцией. Это значение должно быть чем-то, что может быть преобразовано в строку или символ.

Object.groupBy возвращает объект, в котором каждое свойство представляет собой группу, содержащую массив элементов. Использование такого объекта позволяет эргономично проводить деструктуризацию и предотвращает случайные коллизии с глобальными свойствами Object.

Используя тот же набор данных, с помощью Object.groupBy мы можем сгруппировать сотрудников по их отделам:

const groupByDepartment = Object.groupBy(employees, ({department}) => department)

Это дает тот же результат, что и метод reduce, но с меньшим количеством кода. Вывод для groupByDepartment должен быть следующим:

{
    "engineering": [
        {
            "name": "John Doe",
            "department": "engineering",
            "age": 47,
            "joined": "2020-10-03T22:00:00.000Z"
        },
        {
            "name": "Sarah Clara",
            "department": "engineering",
            "age": 32,
            "manager": {
                "name": "John Doe",
                "department": "engineering",
                "age": 47,
                "joined": "2020-10-03T22:00:00.000Z"
            },
            "joined": "2021-10-04T22:00:00.000Z"
        },
        {
            "name": "Funmi Kola",
            "department": "engineering",
            "age": 20,
            "manager": {
                "name": "John Doe",
                "department": "engineering",
                "age": 47,
                "joined": "2020-10-03T22:00:00.000Z"
            },
            "joined": "2022-10-04T22:00:00.000Z"
        }
    ],
    "finance": [
        {
            "name": "Pop Jones Jr.",
            "department": "finance",
            "age": 30,
            "manager": {
                "name": "Anna Maria",
                "department": "finance",
                "age": 45,
                "joined": "2020-10-04T22:00:00.000Z"
            },
            "joined": "2021-10-03T22:00:00.000Z"
        },
        {
            "name": "Anna Maria",
            "department": "finance",
            "age": 45,
            "joined": "2020-10-04T22:00:00.000Z"
        }
    ],
    "sales": [
        {
            "name": "Julius Maria",
            "department": "sales",
            "age": 27,
            "manager": {
                "name": "Anna Maria",
                "department": "finance",
                "age": 45,
                "joined": "2020-10-04T22:00:00.000Z"
            },
            "joined": "2022-10-04T22:00:00.000Z"
        }
    ]
}

Группировка с помощью Map.groupBy

Функция Map.groupBy аналогична Object.groupBy, за исключением того, что она возвращает Map, а заданный итерируемый массив может быть сгруппирован по любому произвольному значению. В данном случае набор данных можно сгруппировать с помощью объекта.

Возвращаясь к нашему набору данных, мы определили свойство manager для некоторых сотрудников и присвоили ему объект ceo или cfo. Мы можем сгруппировать сотрудников по их руководителю (manager), что будет выглядеть следующим образом:

const managerWithTeammates = Map.groupBy(employees, ({manager}) => manager)

Вывод для managerWithTeammates должен выглядеть так:

Map (3) 
{undefined => [{
  "name": "John Doe",
  "department": "engineering",
  "age": 47,
  "joined": "2020-10-03T22:00:00.000Z"
}, {
  "name": "Anna Maria",
  "department": "finance",
  "age": 45,
  "joined": "2020-10-04T22:00:00.000Z"
}], 
{
  "name": "Anna Maria",
  "department": "finance",
  "age": 45,
  "joined": "2020-10-04T22:00:00.000Z"
} => [{
  "name": "Pop Jones Jr.",
  "department": "finance",
  "age": 30,
  "manager": {
    "name": "Anna Maria",
    "department": "finance",
    "age": 45,
    "joined": "2020-10-04T22:00:00.000Z"
  },
  "joined": "2021-10-03T22:00:00.000Z"
}, {
  "name": "Julius Maria",
  "department": "sales",
  "age": 27,
  "manager": {
    "name": "Anna Maria",
    "department": "finance",
    "age": 45,
    "joined": "2020-10-04T22:00:00.000Z"
  },
  "joined": "2022-10-04T22:00:00.000Z"
}], 
{
  "name": "John Doe",
  "department": "engineering",
  "age": 47,
  "joined": "2020-10-03T22:00:00.000Z"
} => [{
  "name": "Sarah Clara",
  "department": "engineering",
  "age": 32,
  "manager": {
    "name": "John Doe",
    "department": "engineering",
    "age": 47,
    "joined": "2020-10-03T22:00:00.000Z"
  },
  "joined": "2021-10-04T22:00:00.000Z"
}, {
  "name": "Funmi Kola",
  "department": "engineering",
  "age": 20,
  "manager": {
    "name": "John Doe",
    "department": "engineering",
    "age": 47,
    "joined": "2020-10-03T22:00:00.000Z"
  },
  "joined": "2022-10-04T22:00:00.000Z"
}]}

В этом примере у нас есть 3 группы:

  1. CEO и CFO (генеральный и финансовый директор), у которых нет руководителя, с ключом объекта Mapundefined.
  2. CFO, Anna Maria, у которой в подчинении два сотрудника. В качестве ключа объекта Map используется объект cfo.
  3. CEO, John Doe, в подчинении которого находятся два сотрудника. В качестве ключа объекта Map используется объект ceo.

Заключение

Разве не удивительны новые API, которые появляются в JavaScript? Функции Object.groupBy и Map.groupBy упрощают группировку данных в массиве или итерируемом файле, и они более эргономичны, чем традиционный метод reduce. Они также уменьшают размер пакета, поскольку не требуют использования внешних библиотек, таких как lodash или underscore.js.

Напомним, что функция Map.groupBy используется в основном для группировки элементов, связанных с любым произвольным объектом, особенно если этот объект может меняться со временем. Если объект неизменен, можно представить его в виде строки и сгруппировать элементы с помощью Object.groupBy(). Если вы используете TypeScript, установите целевой параметр ESNext (или ES2024, когда он будет доступен).

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх